KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint.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) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2015 Wayne Stambaugh <[email protected]>
7 * Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26#include <magic_enum.hpp>
27
28#include <unordered_set>
29
30#include <bitmaps.h>
31#include <board.h>
33#include <confirm.h>
36#include <drc/drc_item.h>
37#include <embedded_files.h>
38#include <font/font.h>
39#include <font/outline_font.h>
40#include <footprint.h>
44#include <i18n_utility.h>
45#include <lset.h>
46#include <macros.h>
47#include <pad.h>
48#include <pcb_dimension.h>
49#include <pcb_edit_frame.h>
50#include <pcb_field.h>
51#include <pcb_group.h>
52#include <pcb_marker.h>
53#include <pcb_reference_image.h>
54#include <pcb_textbox.h>
55#include <pcb_track.h>
56#include <refdes_utils.h>
57#include <string_utils.h>
58#include <view/view.h>
59#include <zone.h>
60
61#include <google/protobuf/any.pb.h>
62#include <api/board/board_types.pb.h>
63#include <api/api_enums.h>
64#include <api/api_utils.h>
65#include <api/api_pcb_utils.h>
66#include <wx/log.h>
67
68
71 m_boundingBoxCacheTimeStamp( 0 ),
72 m_textExcludedBBoxCacheTimeStamp( 0 ),
73 m_hullCacheTimeStamp( 0 ),
74 m_initial_comments( nullptr ),
75 m_componentClass( nullptr )
76{
77 m_attributes = 0;
78 m_layer = F_Cu;
81 m_arflag = 0;
82 m_link = 0;
84 m_zoneConnection = ZONE_CONNECTION::INHERITED;
86 m_embedFonts = false;
87
88 // These are the mandatory fields for the editor to work
89 for( int i = 0; i < MANDATORY_FIELDS; i++ )
90 {
91 PCB_FIELD* field = new PCB_FIELD( this, i );
92 m_fields.push_back( field );
93
94 switch( i )
95 {
96 case REFERENCE_FIELD:
97 field->SetLayer( F_SilkS );
98 field->SetVisible( true );
99 break;
100 case VALUE_FIELD:
101 field->SetLayer( F_Fab );
102 field->SetVisible( true );
103 break;
104 default:
105 field->SetLayer( F_Fab );
106 field->SetVisible( false );
107 break;
108 }
109 }
110
111 m_3D_Drawings.clear();
112}
113
114
115FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
116 BOARD_ITEM_CONTAINER( aFootprint )
117{
118 m_pos = aFootprint.m_pos;
119 m_fpid = aFootprint.m_fpid;
120 m_attributes = aFootprint.m_attributes;
121 m_fpStatus = aFootprint.m_fpStatus;
122 m_orient = aFootprint.m_orient;
123 m_lastEditTime = aFootprint.m_lastEditTime;
124 m_link = aFootprint.m_link;
125 m_path = aFootprint.m_path;
126 m_embedFonts = aFootprint.m_embedFonts;
127
132 m_cachedHull = aFootprint.m_cachedHull;
134
135 m_clearance = aFootprint.m_clearance;
142
144
145 std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
146
147 // Copy fields
148 for( PCB_FIELD* field : aFootprint.Fields() )
149 {
150 PCB_FIELD* newField = static_cast<PCB_FIELD*>( field->Clone() );
151 ptrMap[field] = newField;
152 Add( newField, ADD_MODE::APPEND ); // Append to ensure indexes are identical
153 }
154
155 // Copy pads
156 for( PAD* pad : aFootprint.Pads() )
157 {
158 PAD* newPad = static_cast<PAD*>( pad->Clone() );
159 ptrMap[ pad ] = newPad;
160 Add( newPad, ADD_MODE::APPEND ); // Append to ensure indexes are identical
161 }
162
163 // Copy zones
164 for( ZONE* zone : aFootprint.Zones() )
165 {
166 ZONE* newZone = static_cast<ZONE*>( zone->Clone() );
167 ptrMap[ zone ] = newZone;
168 Add( newZone, ADD_MODE::APPEND ); // Append to ensure indexes are identical
169
170 // Ensure the net info is OK and especially uses the net info list
171 // living in the current board
172 // Needed when copying a fp from fp editor that has its own board
173 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
174 newZone->SetNetCode( -1 );
175 }
176
177 // Copy drawings
178 for( BOARD_ITEM* item : aFootprint.GraphicalItems() )
179 {
180 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
181 ptrMap[ item ] = newItem;
182 Add( newItem, ADD_MODE::APPEND ); // Append to ensure indexes are identical
183 }
184
185 // Copy groups
186 for( PCB_GROUP* group : aFootprint.Groups() )
187 {
188 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
189 ptrMap[ group ] = newGroup;
190 Add( newGroup, ADD_MODE::APPEND ); // Append to ensure indexes are identical
191 }
192
193 // Rebuild groups
194 for( PCB_GROUP* group : aFootprint.Groups() )
195 {
196 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( ptrMap[ group ] );
197
198 newGroup->GetItems().clear();
199
200 for( BOARD_ITEM* member : group->GetItems() )
201 {
202 if( ptrMap.count( member ) )
203 newGroup->AddItem( ptrMap[ member ] );
204 }
205 }
206
207 for( auto& [ name, file ] : aFootprint.EmbeddedFileMap() )
209
210 // Copy auxiliary data
211 m_3D_Drawings = aFootprint.m_3D_Drawings;
213 m_keywords = aFootprint.m_keywords;
214 m_privateLayers = aFootprint.m_privateLayers;
215
216 m_arflag = 0;
217
219 new wxArrayString( *aFootprint.m_initial_comments ) : nullptr;
220}
221
222
224 BOARD_ITEM_CONTAINER( aFootprint )
225{
226 *this = std::move( aFootprint );
227}
228
229
231{
232 // Untangle group parents before doing any deleting
233 for( PCB_GROUP* group : m_groups )
234 {
235 for( BOARD_ITEM* item : group->GetItems() )
236 item->SetParentGroup( nullptr );
237 }
238
239 // Clean up the owned elements
240 delete m_initial_comments;
241
242 for( PCB_FIELD* f : m_fields )
243 delete f;
244
245 m_fields.clear();
246
247 for( PAD* p : m_pads )
248 delete p;
249
250 m_pads.clear();
251
252 for( ZONE* zone : m_zones )
253 delete zone;
254
255 m_zones.clear();
256
257 for( PCB_GROUP* group : m_groups )
258 delete group;
259
260 m_groups.clear();
261
262 for( BOARD_ITEM* d : m_drawings )
263 delete d;
264
265 m_drawings.clear();
266
267 if( BOARD* board = GetBoard() )
268 board->IncrementTimeStamp();
269}
270
271
272void FOOTPRINT::Serialize( google::protobuf::Any &aContainer ) const
273{
274 using namespace kiapi::board;
275 types::FootprintInstance footprint;
276
277 footprint.mutable_id()->set_value( m_Uuid.AsStdString() );
278 footprint.mutable_position()->set_x_nm( GetPosition().x );
279 footprint.mutable_position()->set_y_nm( GetPosition().y );
280 footprint.mutable_orientation()->set_value_degrees( GetOrientationDegrees() );
281 footprint.set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( GetLayer() ) );
282 footprint.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
283 : kiapi::common::types::LockedState::LS_UNLOCKED );
284
285 google::protobuf::Any buf;
287 buf.UnpackTo( footprint.mutable_reference_field() );
288 GetField( VALUE_FIELD )->Serialize( buf );
289 buf.UnpackTo( footprint.mutable_value_field() );
291 buf.UnpackTo( footprint.mutable_datasheet_field() );
293 buf.UnpackTo( footprint.mutable_description_field() );
294
295 types::FootprintAttributes* attrs = footprint.mutable_attributes();
296
297 attrs->set_not_in_schematic( IsBoardOnly() );
298 attrs->set_exclude_from_position_files( IsExcludedFromPosFiles() );
299 attrs->set_exclude_from_bill_of_materials( IsExcludedFromBOM() );
300 attrs->set_exempt_from_courtyard_requirement( AllowMissingCourtyard() );
301 attrs->set_do_not_populate( IsDNP() );
302
303 types::Footprint* def = footprint.mutable_definition();
304
305 def->mutable_id()->CopyFrom( kiapi::common::LibIdToProto( GetFPID() ) );
306 // anchor?
307 def->mutable_attributes()->set_description( GetLibDescription().ToStdString() );
308 def->mutable_attributes()->set_keywords( GetKeywords().ToStdString() );
309
310 // TODO: serialize library mandatory fields
311
312 types::FootprintDesignRuleOverrides* overrides = def->mutable_overrides();
313
314 if( GetLocalClearance().has_value() )
315 overrides->mutable_copper_clearance()->set_value_nm( *GetLocalClearance() );
316
317 if( GetLocalSolderMaskMargin().has_value() )
318 overrides->mutable_solder_mask()->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() );
319
320 if( GetLocalSolderPasteMargin().has_value() )
321 overrides->mutable_solder_paste()->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() );
322
323 if( GetLocalSolderPasteMarginRatio().has_value() )
324 overrides->mutable_solder_paste()->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() );
325
326 overrides->set_zone_connection(
327 ToProtoEnum<ZONE_CONNECTION, types::ZoneConnectionStyle>( GetLocalZoneConnection() ) );
328
329 for( const wxString& group : GetNetTiePadGroups() )
330 {
331 types::NetTieDefinition* netTie = def->add_net_ties();
332 wxStringTokenizer tokenizer( group, " " );
333
334 while( tokenizer.HasMoreTokens() )
335 netTie->add_pad_number( tokenizer.GetNextToken().ToStdString() );
336 }
337
338 for( PCB_LAYER_ID layer : GetPrivateLayers().Seq() )
339 def->add_private_layers( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
340
341 for( const PCB_FIELD* item : Fields() )
342 {
343 if( item->GetId() < MANDATORY_FIELDS )
344 continue;
345
346 google::protobuf::Any* itemMsg = def->add_items();
347 item->Serialize( *itemMsg );
348 }
349
350 for( const PAD* item : Pads() )
351 {
352 google::protobuf::Any* itemMsg = def->add_items();
353 item->Serialize( *itemMsg );
354 }
355
356 for( const BOARD_ITEM* item : GraphicalItems() )
357 {
358 google::protobuf::Any* itemMsg = def->add_items();
359 item->Serialize( *itemMsg );
360 }
361
362 for( const ZONE* item : Zones() )
363 {
364 google::protobuf::Any* itemMsg = def->add_items();
365 item->Serialize( *itemMsg );
366 }
367
368 for( const FP_3DMODEL& model : Models() )
369 {
370 google::protobuf::Any* itemMsg = def->add_items();
371 types::Footprint3DModel modelMsg;
372 modelMsg.set_filename( model.m_Filename.ToUTF8() );
373 kiapi::common::PackVector3D( *modelMsg.mutable_scale(), model.m_Scale );
374 kiapi::common::PackVector3D( *modelMsg.mutable_rotation(), model.m_Rotation );
375 kiapi::common::PackVector3D( *modelMsg.mutable_offset(), model.m_Offset );
376 modelMsg.set_visible( model.m_Show );
377 modelMsg.set_opacity( model.m_Opacity );
378 itemMsg->PackFrom( modelMsg );
379 }
380
381 aContainer.PackFrom( footprint );
382}
383
384
385bool FOOTPRINT::Deserialize( const google::protobuf::Any &aContainer )
386{
387 using namespace kiapi::board;
388 types::FootprintInstance footprint;
389
390 if( !aContainer.UnpackTo( &footprint ) )
391 return false;
392
393 const_cast<KIID&>( m_Uuid ) = KIID( footprint.id().value() );
394 SetPosition( VECTOR2I( footprint.position().x_nm(), footprint.position().y_nm() ) );
395 SetOrientationDegrees( footprint.orientation().value_degrees() );
396 SetLayer( FromProtoEnum<PCB_LAYER_ID, types::BoardLayer>( footprint.layer() ) );
397 SetLocked( footprint.locked() == kiapi::common::types::LockedState::LS_LOCKED );
398
399 google::protobuf::Any buf;
400 types::Field mandatoryField;
401
402 if( footprint.has_reference_field() )
403 {
404 mandatoryField = footprint.reference_field();
405 mandatoryField.mutable_id()->set_id( REFERENCE_FIELD );
406 buf.PackFrom( mandatoryField );
408 }
409
410 if( footprint.has_value_field() )
411 {
412 mandatoryField = footprint.value_field();
413 mandatoryField.mutable_id()->set_id( VALUE_FIELD );
414 buf.PackFrom( mandatoryField );
416 }
417
418 if( footprint.has_datasheet_field() )
419 {
420 mandatoryField = footprint.datasheet_field();
421 mandatoryField.mutable_id()->set_id( DATASHEET_FIELD );
422 buf.PackFrom( mandatoryField );
424 }
425
426 if( footprint.has_description_field() )
427 {
428 mandatoryField = footprint.description_field();
429 mandatoryField.mutable_id()->set_id( DESCRIPTION_FIELD );
430 buf.PackFrom( mandatoryField );
432 }
433
434 SetBoardOnly( footprint.attributes().not_in_schematic() );
435 SetExcludedFromBOM( footprint.attributes().exclude_from_bill_of_materials() );
436 SetExcludedFromPosFiles( footprint.attributes().exclude_from_position_files() );
437 SetAllowMissingCourtyard( footprint.attributes().exempt_from_courtyard_requirement() );
438 SetDNP( footprint.attributes().do_not_populate() );
439
440 // Definition
441 SetFPID( kiapi::common::LibIdFromProto( footprint.definition().id() ) );
442 // TODO: how should anchor be handled?
443 SetLibDescription( footprint.definition().attributes().description() );
444 SetKeywords( footprint.definition().attributes().keywords() );
445
446 const types::FootprintDesignRuleOverrides& overrides = footprint.overrides();
447
448 if( overrides.has_copper_clearance() )
449 SetLocalClearance( overrides.copper_clearance().value_nm() );
450 else
451 SetLocalClearance( std::nullopt );
452
453 if( overrides.has_solder_mask() && overrides.solder_mask().has_solder_mask_margin() )
454 SetLocalSolderMaskMargin( overrides.solder_mask().solder_mask_margin().value_nm() );
455 else
456 SetLocalSolderMaskMargin( std::nullopt );
457
458 if( overrides.has_solder_paste() )
459 {
460 const types::SolderPasteOverrides& pasteSettings = overrides.solder_paste();
461
462 if( pasteSettings.has_solder_paste_margin() )
463 SetLocalSolderPasteMargin( pasteSettings.solder_paste_margin().value_nm() );
464 else
465 SetLocalSolderPasteMargin( std::nullopt );
466
467 if( pasteSettings.has_solder_paste_margin_ratio() )
468 SetLocalSolderPasteMarginRatio( pasteSettings.solder_paste_margin_ratio().value() );
469 else
470 SetLocalSolderPasteMarginRatio( std::nullopt );
471 }
472
473 SetLocalZoneConnection( FromProtoEnum<ZONE_CONNECTION>( overrides.zone_connection() ) );
474
475 for( const types::NetTieDefinition& netTieMsg : footprint.definition().net_ties() )
476 {
477 wxString group;
478
479 for( const std::string& pad : netTieMsg.pad_number() )
480 group.Append( wxString::Format( wxT( "%s " ), pad ) );
481
482 group.Trim();
484 }
485
486 LSET privateLayers;
487
488 for( int layerMsg : footprint.definition().private_layers() )
489 {
490 auto layer = static_cast<types::BoardLayer>( layerMsg );
491 privateLayers.set( FromProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
492 }
493
494 SetPrivateLayers( privateLayers );
495
496 // Footprint items
497 for( PCB_FIELD* field : Fields() )
498 {
499 if( field->GetId() >= MANDATORY_FIELDS )
500 Remove( field );
501 }
502
503 Pads().clear();
504 GraphicalItems().clear();
505 Zones().clear();
506 Groups().clear();
507 Models().clear();
508
509 for( const google::protobuf::Any& itemMsg : footprint.definition().items() )
510 {
511 std::optional<KICAD_T> type = kiapi::common::TypeNameFromAny( itemMsg );
512
513 if( !type )
514 {
515 // Bit of a hack here, but eventually 3D models should be promoted to a first-class
516 // object, at which point they can get their own serialization
517 if( itemMsg.type_url() == "type.googleapis.com/kiapi.board.types.Footprint3DModel" )
518 {
519 types::Footprint3DModel modelMsg;
520
521 if( !itemMsg.UnpackTo( &modelMsg ) )
522 continue;
523
524 FP_3DMODEL model;
525
526 model.m_Filename = wxString::FromUTF8( modelMsg.filename() );
527 model.m_Show = modelMsg.visible();
528 model.m_Opacity = modelMsg.opacity();
529 model.m_Scale = kiapi::common::UnpackVector3D( modelMsg.scale() );
530 model.m_Rotation = kiapi::common::UnpackVector3D( modelMsg.rotation() );
531 model.m_Offset = kiapi::common::UnpackVector3D( modelMsg.offset() );
532
533 Models().push_back( model );
534 }
535 else
536 {
537 wxLogTrace( traceApi, wxString::Format( wxS( "Attempting to unpack unknown type %s "
538 "from footprint message, skipping" ),
539 itemMsg.type_url() ) );
540 }
541
542 continue;
543 }
544
545 std::unique_ptr<BOARD_ITEM> item = CreateItemForType( *type, this );
546
547 if( item && item->Deserialize( itemMsg ) )
548 Add( item.release(), ADD_MODE::APPEND );
549 }
550
551 return true;
552}
553
554
556{
557 return m_fields[aFieldType];
558}
559
560
562{
563 return m_fields[aFieldType];
564}
565
566
568{
569 for( PCB_FIELD* field : m_fields )
570 {
571 if( field->GetId() == aFieldId )
572 return field;
573 }
574
575 return nullptr;
576}
577
578bool FOOTPRINT::HasFieldByName( const wxString& aFieldName ) const
579{
580 for( PCB_FIELD* field : m_fields )
581 {
582 if( field->GetCanonicalName() == aFieldName )
583 return true;
584 }
585
586 return false;
587}
588
589PCB_FIELD* FOOTPRINT::GetFieldByName( const wxString& aFieldName )
590{
591 if( aFieldName.empty() )
592 return nullptr;
593
594 for( PCB_FIELD* field : m_fields )
595 {
596 if( field->GetName() == aFieldName )
597 return field;
598 }
599
600 return nullptr;
601}
602
603
604wxString FOOTPRINT::GetFieldText( const wxString& aFieldName ) const
605{
606 for( const PCB_FIELD* field : m_fields )
607 {
608 if( aFieldName == field->GetName() || aFieldName == field->GetCanonicalName() )
609 return field->GetText();
610 }
611
612 return wxEmptyString;
613}
614
615
616void FOOTPRINT::GetFields( std::vector<PCB_FIELD*>& aVector, bool aVisibleOnly )
617{
618 for( PCB_FIELD* field : m_fields )
619 {
620 // FOOTPRINT field doesn't exist on FOOTPRINT, but it's kept in the m_fields vector
621 // for the moment so that the MANDATORY_FIELD ids line up with vector indices.
622 // This could be cleaned up by making m_fields a map in the future.
623 if( field->GetId() == FOOTPRINT_FIELD )
624 continue;
625
626 if( aVisibleOnly )
627 {
628 if( !field->IsVisible() || field->GetText().IsEmpty() )
629 continue;
630 }
631
632 aVector.push_back( field );
633 }
634}
635
636
638{
639 int newNdx = m_fields.size();
640
641 m_fields.push_back( new PCB_FIELD( aField ) );
642 return m_fields[newNdx];
643}
644
645
646void FOOTPRINT::RemoveField( const wxString& aFieldName )
647{
648 for( unsigned i = MANDATORY_FIELDS; i < m_fields.size(); ++i )
649 {
650 if( aFieldName == m_fields[i]->GetName( false ) )
651 {
652 m_fields.erase( m_fields.begin() + i );
653 return;
654 }
655 }
656}
657
658
659void FOOTPRINT::ApplyDefaultSettings( const BOARD& board, bool aStyleFields, bool aStyleText,
660 bool aStyleShapes )
661{
662 if( aStyleFields )
663 {
664 for( PCB_FIELD* field : m_fields )
665 field->StyleFromSettings( board.GetDesignSettings() );
666 }
667
668 for( BOARD_ITEM* item : m_drawings )
669 {
670 switch( item->Type() )
671 {
672 case PCB_TEXT_T:
673 case PCB_TEXTBOX_T:
674 if( aStyleText )
675 item->StyleFromSettings( board.GetDesignSettings() );
676
677 break;
678
679 case PCB_SHAPE_T:
680 if( aStyleShapes && !item->IsOnCopperLayer() )
681 item->StyleFromSettings( board.GetDesignSettings() );
682
683 break;
684
685 default:
686 break;
687 }
688 }
689}
690
691
693{
694 // replace null UUIDs if any by a valid uuid
695 std::vector< BOARD_ITEM* > item_list;
696
697 for( PCB_FIELD* field : m_fields )
698 item_list.push_back( field );
699
700 for( PAD* pad : m_pads )
701 item_list.push_back( pad );
702
703 for( BOARD_ITEM* gr_item : m_drawings )
704 item_list.push_back( gr_item );
705
706 // Note: one cannot fix null UUIDs inside the group, but it should not happen
707 // because null uuids can be found in old footprints, therefore without group
708 for( PCB_GROUP* group : m_groups )
709 item_list.push_back( group );
710
711 // Probably notneeded, because old fp do not have zones. But just in case.
712 for( ZONE* zone : m_zones )
713 item_list.push_back( zone );
714
715 bool changed = false;
716
717 for( BOARD_ITEM* item : item_list )
718 {
719 if( item->m_Uuid == niluuid )
720 {
721 const_cast<KIID&>( item->m_Uuid ) = KIID();
722 changed = true;
723 }
724 }
725
726 return changed;
727}
728
729
731{
732 BOARD_ITEM::operator=( aOther );
733
734 m_pos = aOther.m_pos;
735 m_fpid = aOther.m_fpid;
736 m_attributes = aOther.m_attributes;
737 m_fpStatus = aOther.m_fpStatus;
738 m_orient = aOther.m_orient;
739 m_lastEditTime = aOther.m_lastEditTime;
740 m_link = aOther.m_link;
741 m_path = aOther.m_path;
742
743 m_cachedBoundingBox = aOther.m_cachedBoundingBox;
744 m_boundingBoxCacheTimeStamp = aOther.m_boundingBoxCacheTimeStamp;
745 m_cachedTextExcludedBBox = aOther.m_cachedTextExcludedBBox;
746 m_textExcludedBBoxCacheTimeStamp = aOther.m_textExcludedBBoxCacheTimeStamp;
747 m_cachedHull = aOther.m_cachedHull;
748 m_hullCacheTimeStamp = aOther.m_hullCacheTimeStamp;
749
750 m_clearance = aOther.m_clearance;
751 m_solderMaskMargin = aOther.m_solderMaskMargin;
752 m_solderPasteMargin = aOther.m_solderPasteMargin;
753 m_solderPasteMarginRatio = aOther.m_solderPasteMarginRatio;
754 m_zoneConnection = aOther.m_zoneConnection;
755 m_netTiePadGroups = aOther.m_netTiePadGroups;
756
757 m_componentClass = aOther.m_componentClass;
758
759 // Move the fields
760 m_fields.clear();
761
762 for( PCB_FIELD* field : aOther.Fields() )
763 Add( field );
764
765 // Move the pads
766 m_pads.clear();
767
768 for( PAD* pad : aOther.Pads() )
769 Add( pad );
770
771 aOther.Pads().clear();
772
773 // Move the zones
774 m_zones.clear();
775
776 for( ZONE* item : aOther.Zones() )
777 {
778 Add( item );
779
780 // Ensure the net info is OK and especially uses the net info list
781 // living in the current board
782 // Needed when copying a fp from fp editor that has its own board
783 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
784 item->SetNetCode( -1 );
785 }
786
787 aOther.Zones().clear();
788
789 // Move the drawings
790 m_drawings.clear();
791
792 for( BOARD_ITEM* item : aOther.GraphicalItems() )
793 Add( item );
794
795 aOther.GraphicalItems().clear();
796
797 // Move the groups
798 m_groups.clear();
799
800 for( PCB_GROUP* group : aOther.Groups() )
801 Add( group );
802
803 aOther.Groups().clear();
804
805 // Copy auxiliary data
806 m_3D_Drawings = aOther.m_3D_Drawings;
807 m_libDescription = aOther.m_libDescription;
808 m_keywords = aOther.m_keywords;
809 m_privateLayers = aOther.m_privateLayers;
810
811 m_initial_comments = aOther.m_initial_comments;
812
813 // Clear the other item's containers since this is a move
814 aOther.Fields().clear();
815 aOther.Pads().clear();
816 aOther.Zones().clear();
817 aOther.GraphicalItems().clear();
818 aOther.m_initial_comments = nullptr;
819
820 return *this;
821}
822
823
825{
826 BOARD_ITEM::operator=( aOther );
827
828 m_pos = aOther.m_pos;
829 m_fpid = aOther.m_fpid;
830 m_attributes = aOther.m_attributes;
831 m_fpStatus = aOther.m_fpStatus;
832 m_orient = aOther.m_orient;
834 m_link = aOther.m_link;
835 m_path = aOther.m_path;
836
841 m_cachedHull = aOther.m_cachedHull;
843
844 m_clearance = aOther.m_clearance;
850
852
853 std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
854
855 // Copy fields
856 m_fields.clear();
857
858 for( PCB_FIELD* field : aOther.GetFields() )
859 {
860 PCB_FIELD* newField = new PCB_FIELD( *field );
861 ptrMap[field] = newField;
862 Add( newField );
863 }
864
865 // Copy pads
866 m_pads.clear();
867
868 for( PAD* pad : aOther.Pads() )
869 {
870 PAD* newPad = new PAD( *pad );
871 ptrMap[ pad ] = newPad;
872 Add( newPad );
873 }
874
875 // Copy zones
876 m_zones.clear();
877
878 for( ZONE* zone : aOther.Zones() )
879 {
880 ZONE* newZone = static_cast<ZONE*>( zone->Clone() );
881 ptrMap[ zone ] = newZone;
882 Add( newZone );
883
884 // Ensure the net info is OK and especially uses the net info list
885 // living in the current board
886 // Needed when copying a fp from fp editor that has its own board
887 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
888 newZone->SetNetCode( -1 );
889 }
890
891 // Copy drawings
892 m_drawings.clear();
893
894 for( BOARD_ITEM* item : aOther.GraphicalItems() )
895 {
896 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
897 ptrMap[ item ] = newItem;
898 Add( newItem );
899 }
900
901 // Copy groups
902 m_groups.clear();
903
904 for( PCB_GROUP* group : aOther.Groups() )
905 {
906 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
907 newGroup->GetItems().clear();
908
909 for( BOARD_ITEM* member : group->GetItems() )
910 newGroup->AddItem( ptrMap[ member ] );
911
912 Add( newGroup );
913 }
914
915 // Copy auxiliary data
918 m_keywords = aOther.m_keywords;
920
922 new wxArrayString( *aOther.m_initial_comments ) : nullptr;
923
924 return *this;
925}
926
927
929{
930 return HasFlag( COURTYARD_CONFLICT );
931}
932
933
934void FOOTPRINT::GetContextualTextVars( wxArrayString* aVars ) const
935{
936 aVars->push_back( wxT( "REFERENCE" ) );
937 aVars->push_back( wxT( "VALUE" ) );
938 aVars->push_back( wxT( "LAYER" ) );
939 aVars->push_back( wxT( "FOOTPRINT_LIBRARY" ) );
940 aVars->push_back( wxT( "FOOTPRINT_NAME" ) );
941 aVars->push_back( wxT( "SHORT_NET_NAME(<pad_number>)" ) );
942 aVars->push_back( wxT( "NET_NAME(<pad_number>)" ) );
943 aVars->push_back( wxT( "NET_CLASS(<pad_number>)" ) );
944 aVars->push_back( wxT( "PIN_NAME(<pad_number>)" ) );
945}
946
947
948bool FOOTPRINT::ResolveTextVar( wxString* token, int aDepth ) const
949{
950 if( GetBoard() && GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
951 return false;
952
953 if( token->IsSameAs( wxT( "REFERENCE" ) ) )
954 {
955 *token = Reference().GetShownText( false, aDepth + 1 );
956 return true;
957 }
958 else if( token->IsSameAs( wxT( "VALUE" ) ) )
959 {
960 *token = Value().GetShownText( false, aDepth + 1 );
961 return true;
962 }
963 else if( token->IsSameAs( wxT( "LAYER" ) ) )
964 {
965 *token = GetLayerName();
966 return true;
967 }
968 else if( token->IsSameAs( wxT( "FOOTPRINT_LIBRARY" ) ) )
969 {
971 return true;
972 }
973 else if( token->IsSameAs( wxT( "FOOTPRINT_NAME" ) ) )
974 {
976 return true;
977 }
978 else if( token->StartsWith( wxT( "SHORT_NET_NAME(" ) )
979 || token->StartsWith( wxT( "NET_NAME(" ) )
980 || token->StartsWith( wxT( "NET_CLASS(" ) )
981 || token->StartsWith( wxT( "PIN_NAME(" ) ) )
982 {
983 wxString padNumber = token->AfterFirst( '(' );
984 padNumber = padNumber.BeforeLast( ')' );
985
986 for( PAD* pad : Pads() )
987 {
988 if( pad->GetNumber() == padNumber )
989 {
990 if( token->StartsWith( wxT( "SHORT_NET_NAME" ) ) )
991 *token = pad->GetShortNetname();
992 else if( token->StartsWith( wxT( "NET_NAME" ) ) )
993 *token = pad->GetNetname();
994 else if( token->StartsWith( wxT( "NET_CLASS" ) ) )
995 *token = pad->GetNetClassVariableSubstitutionName();
996 else
997 *token = pad->GetPinFunction();
998
999 return true;
1000 }
1001 }
1002 }
1003 else if( HasFieldByName( *token ) )
1004 {
1005 *token = GetFieldText( *token );
1006 return true;
1007 }
1008
1009 if( GetBoard() && GetBoard()->ResolveTextVar( token, aDepth + 1 ) )
1010 return true;
1011
1012 return false;
1013}
1014
1015
1017{
1018 // Force the ORPHANED dummy net info for all pads.
1019 // ORPHANED dummy net does not depend on a board
1020 for( PAD* pad : m_pads )
1021 pad->SetNetCode( NETINFO_LIST::ORPHANED );
1022}
1023
1024
1025void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
1026{
1027 switch( aBoardItem->Type() )
1028 {
1029 case PCB_FIELD_T:
1030 // Always append fields
1031 m_fields.push_back( static_cast<PCB_FIELD*>( aBoardItem ) );
1032
1033 break;
1034
1035 case PCB_TEXT_T:
1036 case PCB_DIM_ALIGNED_T:
1037 case PCB_DIM_LEADER_T:
1038 case PCB_DIM_CENTER_T:
1039 case PCB_DIM_RADIAL_T:
1041 case PCB_SHAPE_T:
1042 case PCB_TEXTBOX_T:
1043 case PCB_TABLE_T:
1045 if( aMode == ADD_MODE::APPEND )
1046 m_drawings.push_back( aBoardItem );
1047 else
1048 m_drawings.push_front( aBoardItem );
1049 break;
1050
1051 case PCB_PAD_T:
1052 if( aMode == ADD_MODE::APPEND )
1053 m_pads.push_back( static_cast<PAD*>( aBoardItem ) );
1054 else
1055 m_pads.push_front( static_cast<PAD*>( aBoardItem ) );
1056 break;
1057
1058 case PCB_ZONE_T:
1059 if( aMode == ADD_MODE::APPEND )
1060 m_zones.push_back( static_cast<ZONE*>( aBoardItem ) );
1061 else
1062 m_zones.insert( m_zones.begin(), static_cast<ZONE*>( aBoardItem ) );
1063 break;
1064
1065 case PCB_GROUP_T:
1066 if( aMode == ADD_MODE::APPEND )
1067 m_groups.push_back( static_cast<PCB_GROUP*>( aBoardItem ) );
1068 else
1069 m_groups.insert( m_groups.begin(), static_cast<PCB_GROUP*>( aBoardItem ) );
1070 break;
1071
1072 default:
1073 {
1074 wxString msg;
1075 msg.Printf( wxT( "FOOTPRINT::Add() needs work: BOARD_ITEM type (%d) not handled" ),
1076 aBoardItem->Type() );
1077 wxFAIL_MSG( msg );
1078
1079 return;
1080 }
1081 }
1082
1083 aBoardItem->ClearEditFlags();
1084 aBoardItem->SetParent( this );
1085}
1086
1087
1088void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
1089{
1090 switch( aBoardItem->Type() )
1091 {
1092 case PCB_FIELD_T:
1093 {
1094 for( auto it = m_fields.begin(); it != m_fields.end(); ++it )
1095 {
1096 if( *it == aBoardItem )
1097 {
1098 m_fields.erase( it );
1099 break;
1100 }
1101 }
1102 }
1103 break;
1104
1105 case PCB_TEXT_T:
1106 case PCB_DIM_ALIGNED_T:
1107 case PCB_DIM_CENTER_T:
1109 case PCB_DIM_RADIAL_T:
1110 case PCB_DIM_LEADER_T:
1111 case PCB_SHAPE_T:
1112 case PCB_TEXTBOX_T:
1113 case PCB_TABLE_T:
1115 for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
1116 {
1117 if( *it == aBoardItem )
1118 {
1119 m_drawings.erase( it );
1120 break;
1121 }
1122 }
1123
1124 break;
1125
1126 case PCB_PAD_T:
1127 for( auto it = m_pads.begin(); it != m_pads.end(); ++it )
1128 {
1129 if( *it == static_cast<PAD*>( aBoardItem ) )
1130 {
1131 m_pads.erase( it );
1132 break;
1133 }
1134 }
1135
1136 break;
1137
1138 case PCB_ZONE_T:
1139 for( auto it = m_zones.begin(); it != m_zones.end(); ++it )
1140 {
1141 if( *it == static_cast<ZONE*>( aBoardItem ) )
1142 {
1143 m_zones.erase( it );
1144 break;
1145 }
1146 }
1147
1148 break;
1149
1150 case PCB_GROUP_T:
1151 for( auto it = m_groups.begin(); it != m_groups.end(); ++it )
1152 {
1153 if( *it == static_cast<PCB_GROUP*>( aBoardItem ) )
1154 {
1155 m_groups.erase( it );
1156 break;
1157 }
1158 }
1159
1160 break;
1161
1162 default:
1163 {
1164 wxString msg;
1165 msg.Printf( wxT( "FOOTPRINT::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
1166 aBoardItem->Type() );
1167 wxFAIL_MSG( msg );
1168 }
1169 }
1170
1171 aBoardItem->SetFlags( STRUCT_DELETED );
1172
1173 PCB_GROUP* parentGroup = aBoardItem->GetParentGroup();
1174
1175 if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
1176 parentGroup->RemoveItem( aBoardItem );
1177}
1178
1179
1180double FOOTPRINT::GetArea( int aPadding ) const
1181{
1182 BOX2I bbox = GetBoundingBox( false );
1183
1184 double w = std::abs( static_cast<double>( bbox.GetWidth() ) ) + aPadding;
1185 double h = std::abs( static_cast<double>( bbox.GetHeight() ) ) + aPadding;
1186 return w * h;
1187}
1188
1189
1191{
1192 int smd_count = 0;
1193 int tht_count = 0;
1194
1195 for( PAD* pad : m_pads )
1196 {
1197 switch( pad->GetProperty() )
1198 {
1199 case PAD_PROP::FIDUCIAL_GLBL:
1200 case PAD_PROP::FIDUCIAL_LOCAL:
1201 continue;
1202
1203 case PAD_PROP::HEATSINK:
1204 case PAD_PROP::CASTELLATED:
1205 case PAD_PROP::MECHANICAL:
1206 continue;
1207
1208 case PAD_PROP::NONE:
1209 case PAD_PROP::BGA:
1210 case PAD_PROP::TESTPOINT:
1211 break;
1212 }
1213
1214 switch( pad->GetAttribute() )
1215 {
1216 case PAD_ATTRIB::PTH:
1217 tht_count++;
1218 break;
1219
1220 case PAD_ATTRIB::SMD:
1221 if( pad->IsOnCopperLayer() )
1222 smd_count++;
1223
1224 break;
1225
1226 default:
1227 break;
1228 }
1229 }
1230
1231 // Footprints with plated through-hole pads should usually be marked through hole even if they
1232 // also have SMD because they might not be auto-placed. Exceptions to this might be shielded
1233 if( tht_count > 0 )
1234 return FP_THROUGH_HOLE;
1235
1236 if( smd_count > 0 )
1237 return FP_SMD;
1238
1239 return 0;
1240}
1241
1242
1244{
1245 if( ( m_attributes & FP_SMD ) == FP_SMD )
1246 return _( "SMD" );
1247
1249 return _( "Through hole" );
1250
1251 return _( "Other" );
1252}
1253
1254
1256{
1257 BOX2I bbox;
1258
1259 // We want the bounding box of the footprint pads at rot 0, not flipped
1260 // Create such a image:
1261 FOOTPRINT dummy( *this );
1262
1263 dummy.SetPosition( VECTOR2I( 0, 0 ) );
1264 dummy.SetOrientation( ANGLE_0 );
1265
1266 if( dummy.IsFlipped() )
1267 dummy.Flip( VECTOR2I( 0, 0 ), FLIP_DIRECTION::TOP_BOTTOM );
1268
1269 for( PAD* pad : dummy.Pads() )
1270 bbox.Merge( pad->GetBoundingBox() );
1271
1272 // Remove the parent and the group from the dummy footprint before deletion
1273 dummy.SetParent( nullptr );
1274 dummy.SetParentGroup( nullptr );
1275
1276 return bbox;
1277}
1278
1279
1281{
1282 for( BOARD_ITEM* item : m_drawings )
1283 {
1284 if( m_privateLayers.test( item->GetLayer() ) )
1285 continue;
1286
1287 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_TEXT_T )
1288 return false;
1289 }
1290
1291 return true;
1292}
1293
1294
1296{
1297 return GetBoundingBox( true );
1298}
1299
1300
1301const BOX2I FOOTPRINT::GetBoundingBox( bool aIncludeText ) const
1302{
1303 const BOARD* board = GetBoard();
1304
1305 if( board )
1306 {
1307 if( aIncludeText )
1308 {
1310 return m_cachedBoundingBox;
1311 }
1312 else
1313 {
1316 }
1317 }
1318
1319 std::vector<PCB_TEXT*> texts;
1320 bool isFPEdit = board && board->IsFootprintHolder();
1321
1322 BOX2I bbox( m_pos );
1323 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Give a min size to the bbox
1324
1325 // Calculate the footprint side
1326 PCB_LAYER_ID footprintSide = GetSide();
1327
1328 for( BOARD_ITEM* item : m_drawings )
1329 {
1330 if( m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
1331 continue;
1332
1333 // We want the bitmap bounding box just in the footprint editor
1334 // so it will start with the correct initial zoom
1335 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
1336 continue;
1337
1338 // Handle text separately
1339 if( item->Type() == PCB_TEXT_T )
1340 {
1341 texts.push_back( static_cast<PCB_TEXT*>( item ) );
1342 continue;
1343 }
1344
1345 // If we're not including text then drop annotations as well -- unless, of course, it's
1346 // an unsided footprint -- in which case it's likely to be nothing *but* annotations.
1347 if( !aIncludeText && footprintSide != UNDEFINED_LAYER )
1348 {
1349 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
1350 continue;
1351
1352 if( item->GetLayer() == Cmts_User || item->GetLayer() == Dwgs_User
1353 || item->GetLayer() == Eco1_User || item->GetLayer() == Eco2_User )
1354 {
1355 continue;
1356 }
1357 }
1358
1359 bbox.Merge( item->GetBoundingBox() );
1360 }
1361
1362 for( PCB_FIELD* field : m_fields )
1363 {
1364 // Reference and value get their own processing
1365 if( !field->IsReference() && !field->IsValue() )
1366 texts.push_back( field );
1367 }
1368
1369 for( PAD* pad : m_pads )
1370 bbox.Merge( pad->GetBoundingBox() );
1371
1372 for( ZONE* zone : m_zones )
1373 bbox.Merge( zone->GetBoundingBox() );
1374
1375 bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_zones.empty() );
1376
1377 // Groups do not contribute to the rect, only their members
1378 if( aIncludeText || noDrawItems )
1379 {
1380 // Only PCB_TEXT and PCB_FIELD items are independently selectable;
1381 // PCB_TEXTBOX items go in with other graphic items above.
1382 for( PCB_TEXT* text : texts )
1383 {
1384 if( !isFPEdit && m_privateLayers.test( text->GetLayer() ) )
1385 continue;
1386
1387 if( text->IsVisible() )
1388 bbox.Merge( text->GetBoundingBox() );
1389 }
1390
1391 // This can be further optimized when aIncludeInvisibleText is true, but currently
1392 // leaving this as is until it's determined there is a noticeable speed hit.
1393 bool valueLayerIsVisible = true;
1394 bool refLayerIsVisible = true;
1395
1396 if( board )
1397 {
1398 // The first "&&" conditional handles the user turning layers off as well as layers
1399 // not being present in the current PCB stackup. Values, references, and all
1400 // footprint text can also be turned off via the GAL meta-layers, so the 2nd and
1401 // 3rd "&&" conditionals handle that.
1402 valueLayerIsVisible = board->IsLayerVisible( Value().GetLayer() )
1404 && board->IsElementVisible( LAYER_FP_TEXT );
1405
1406 refLayerIsVisible = board->IsLayerVisible( Reference().GetLayer() )
1408 && board->IsElementVisible( LAYER_FP_TEXT );
1409 }
1410
1411
1412 if( ( Value().IsVisible() && valueLayerIsVisible ) || noDrawItems )
1413 {
1414 bbox.Merge( Value().GetBoundingBox() );
1415 }
1416
1417 if( ( Reference().IsVisible() && refLayerIsVisible ) || noDrawItems )
1418 {
1419 bbox.Merge( Reference().GetBoundingBox() );
1420 }
1421 }
1422
1423 if( board )
1424 {
1425 if( aIncludeText || noDrawItems )
1426 {
1428 m_cachedBoundingBox = bbox;
1429 }
1430 else
1431 {
1434 }
1435 }
1436
1437 return bbox;
1438}
1439
1440
1442{
1443 std::vector<PCB_TEXT*> texts;
1444 const BOARD* board = GetBoard();
1445 bool isFPEdit = board && board->IsFootprintHolder();
1446
1447 // Start with an uninitialized bounding box
1448 BOX2I bbox;
1449
1450 for( BOARD_ITEM* item : m_drawings )
1451 {
1452 if( m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
1453 continue;
1454
1455 if( ( aLayers & item->GetLayerSet() ).none() )
1456 continue;
1457
1458 // We want the bitmap bounding box just in the footprint editor
1459 // so it will start with the correct initial zoom
1460 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
1461 continue;
1462
1463 bbox.Merge( item->GetBoundingBox() );
1464 }
1465
1466 for( PAD* pad : m_pads )
1467 {
1468 if( ( aLayers & pad->GetLayerSet() ).none() )
1469 continue;
1470
1471 bbox.Merge( pad->GetBoundingBox() );
1472 }
1473
1474 for( ZONE* zone : m_zones )
1475 {
1476 if( ( aLayers & zone->GetLayerSet() ).none() )
1477 continue;
1478
1479 bbox.Merge( zone->GetBoundingBox() );
1480 }
1481
1482 return bbox;
1483}
1484
1485
1487{
1488 const BOARD* board = GetBoard();
1489 bool isFPEdit = board && board->IsFootprintHolder();
1490
1491 if( board )
1492 {
1493 if( m_hullCacheTimeStamp >= board->GetTimeStamp() )
1494 return m_cachedHull;
1495 }
1496
1497 SHAPE_POLY_SET rawPolys;
1498 SHAPE_POLY_SET hull;
1499
1500 for( BOARD_ITEM* item : m_drawings )
1501 {
1502 if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
1503 continue;
1504
1505 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_REFERENCE_IMAGE_T )
1506 {
1507 item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
1508 ERROR_OUTSIDE );
1509 }
1510
1511 // We intentionally exclude footprint fields from the bounding hull.
1512 }
1513
1514 for( PAD* pad : m_pads )
1515 {
1516 pad->Padstack().ForEachUniqueLayer(
1517 [&]( PCB_LAYER_ID aLayer )
1518 {
1519 pad->TransformShapeToPolygon( rawPolys, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
1520 } );
1521
1522 // In case hole is larger than pad
1523 pad->TransformHoleToPolygon( rawPolys, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
1524 }
1525
1526 for( ZONE* zone : m_zones )
1527 {
1528 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
1529 {
1530 const SHAPE_POLY_SET& layerPoly = *zone->GetFilledPolysList( layer );
1531
1532 for( int ii = 0; ii < layerPoly.OutlineCount(); ii++ )
1533 {
1534 const SHAPE_LINE_CHAIN& poly = layerPoly.COutline( ii );
1535 rawPolys.AddOutline( poly );
1536 }
1537 }
1538 }
1539
1540 // If there are some graphic items, build the actual hull.
1541 // However if no items, create a minimal polygon (can happen if a footprint
1542 // is created with no item: it contains only 2 texts.
1543 if( rawPolys.OutlineCount() == 0 )
1544 {
1545 // generate a small dummy rectangular outline around the anchor
1546 const int halfsize = pcbIUScale.mmToIU( 1.0 );
1547
1548 rawPolys.NewOutline();
1549
1550 // add a square:
1551 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
1552 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
1553 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y + halfsize );
1554 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y + halfsize );
1555 }
1556
1557 std::vector<VECTOR2I> convex_hull;
1558 BuildConvexHull( convex_hull, rawPolys );
1559
1562
1563 for( const VECTOR2I& pt : convex_hull )
1564 m_cachedHull.Append( pt );
1565
1566 if( board )
1568
1569 return m_cachedHull;
1570}
1571
1572
1573void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1574{
1575 wxString msg, msg2;
1576
1577 // Don't use GetShownText(); we want to see the variable references here
1578 aList.emplace_back( UnescapeString( Reference().GetText() ),
1579 UnescapeString( Value().GetText() ) );
1580
1581 if( aFrame->IsType( FRAME_FOOTPRINT_VIEWER )
1582 || aFrame->IsType( FRAME_FOOTPRINT_CHOOSER )
1583 || aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
1584 {
1585 size_t padCount = GetPadCount( DO_NOT_INCLUDE_NPTH );
1586
1587 aList.emplace_back( _( "Library" ), GetFPID().GetLibNickname().wx_str() );
1588
1589 aList.emplace_back( _( "Footprint Name" ), GetFPID().GetLibItemName().wx_str() );
1590
1591 aList.emplace_back( _( "Pads" ), wxString::Format( wxT( "%zu" ), padCount ) );
1592
1593 aList.emplace_back( wxString::Format( _( "Doc: %s" ), GetLibDescription() ),
1594 wxString::Format( _( "Keywords: %s" ), GetKeywords() ) );
1595
1596 return;
1597 }
1598
1599 // aFrame is the board editor:
1600
1601 switch( GetSide() )
1602 {
1603 case F_Cu: aList.emplace_back( _( "Board Side" ), _( "Front" ) ); break;
1604 case B_Cu: aList.emplace_back( _( "Board Side" ), _( "Back (Flipped)" ) ); break;
1605 default: /* unsided: user-layers only, etc. */ break;
1606 }
1607
1608 auto addToken = []( wxString* aStr, const wxString& aAttr )
1609 {
1610 if( !aStr->IsEmpty() )
1611 *aStr += wxT( ", " );
1612
1613 *aStr += aAttr;
1614 };
1615
1616 wxString status;
1617 wxString attrs;
1618
1619 if( IsLocked() )
1620 addToken( &status, _( "Locked" ) );
1621
1622 if( m_fpStatus & FP_is_PLACED )
1623 addToken( &status, _( "autoplaced" ) );
1624
1626 addToken( &attrs, _( "not in schematic" ) );
1627
1629 addToken( &attrs, _( "exclude from pos files" ) );
1630
1632 addToken( &attrs, _( "exclude from BOM" ) );
1633
1634 if( m_attributes & FP_DNP )
1635 addToken( &attrs, _( "DNP" ) );
1636
1637 aList.emplace_back( _( "Status: " ) + status, _( "Attributes:" ) + wxS( " " ) + attrs );
1638
1639 aList.emplace_back( _( "Rotation" ), wxString::Format( wxT( "%.4g" ),
1640 GetOrientation().AsDegrees() ) );
1641
1642 if( m_componentClass )
1643 {
1644 aList.emplace_back( _( "Component Class" ), m_componentClass->GetName() );
1645 }
1646
1647 msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
1648 msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" )
1649 : m_3D_Drawings.front().m_Filename );
1650 aList.emplace_back( msg, msg2 );
1651
1652 msg.Printf( _( "Doc: %s" ), m_libDescription );
1653 msg2.Printf( _( "Keywords: %s" ), m_keywords );
1654 aList.emplace_back( msg, msg2 );
1655}
1656
1657
1659{
1660 if( const BOARD* board = GetBoard() )
1661 {
1662 if( board->IsFootprintHolder() )
1663 return UNDEFINED_LAYER;
1664 }
1665
1666 // Test pads first; they're the most likely to return a quick answer.
1667 for( PAD* pad : m_pads )
1668 {
1669 if( ( LSET::SideSpecificMask() & pad->GetLayerSet() ).any() )
1670 return GetLayer();
1671 }
1672
1673 for( BOARD_ITEM* item : m_drawings )
1674 {
1675 if( LSET::SideSpecificMask().test( item->GetLayer() ) )
1676 return GetLayer();
1677 }
1678
1679 for( ZONE* zone : m_zones )
1680 {
1681 if( ( LSET::SideSpecificMask() & zone->GetLayerSet() ).any() )
1682 return GetLayer();
1683 }
1684
1685 return UNDEFINED_LAYER;
1686}
1687
1688
1690{
1691 // If we have any pads, fall back on normal checking
1692 for( PAD* pad : m_pads )
1693 {
1694 if( pad->IsOnLayer( aLayer ) )
1695 return true;
1696 }
1697
1698 for( ZONE* zone : m_zones )
1699 {
1700 if( zone->IsOnLayer( aLayer ) )
1701 return true;
1702 }
1703
1704 for( PCB_FIELD* field : m_fields )
1705 {
1706 if( field->IsOnLayer( aLayer ) )
1707 return true;
1708 }
1709
1710 for( BOARD_ITEM* item : m_drawings )
1711 {
1712 if( item->IsOnLayer( aLayer ) )
1713 return true;
1714 }
1715
1716 return false;
1717}
1718
1719
1720bool FOOTPRINT::HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy ) const
1721{
1722 for( PAD* pad : m_pads )
1723 {
1724 if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition, aAccuracy ) )
1725 return true;
1726 }
1727
1728 for( ZONE* zone : m_zones )
1729 {
1730 if( zone->IsOnLayer( aLayer ) && zone->HitTest( aPosition, aAccuracy ) )
1731 return true;
1732 }
1733
1734 for( BOARD_ITEM* item : m_drawings )
1735 {
1736 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer )
1737 && item->HitTest( aPosition, aAccuracy ) )
1738 {
1739 return true;
1740 }
1741 }
1742
1743 return false;
1744}
1745
1746
1747bool FOOTPRINT::HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy ) const
1748{
1749 std::vector<BOARD_ITEM*> items;
1750
1751 for( PAD* pad : m_pads )
1752 {
1753 if( pad->IsOnLayer( aLayer ) )
1754 items.push_back( pad );
1755 }
1756
1757 for( ZONE* zone : m_zones )
1758 {
1759 if( zone->IsOnLayer( aLayer ) )
1760 items.push_back( zone );
1761 }
1762
1763 for( BOARD_ITEM* item : m_drawings )
1764 {
1765 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) )
1766 items.push_back( item );
1767 }
1768
1769 // If we require the elements to be contained in the rect and any of them are not,
1770 // we can return false;
1771 // Conversely, if we just require any of the elements to have a hit, we can return true
1772 // when the first one is found.
1773 for( BOARD_ITEM* item : items )
1774 {
1775 if( !aContained && item->HitTest( aRect, aContained, aAccuracy ) )
1776 return true;
1777 else if( aContained && !item->HitTest( aRect, aContained, aAccuracy ) )
1778 return false;
1779 }
1780
1781 // If we didn't exit in the loop, that means that we did not return false for aContained or
1782 // we did not return true for !aContained. So we can just return the bool with a test of
1783 // whether there were any elements or not.
1784 return !items.empty() && aContained;
1785}
1786
1787
1788bool FOOTPRINT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1789{
1790 BOX2I rect = GetBoundingBox( false );
1791 return rect.Inflate( aAccuracy ).Contains( aPosition );
1792}
1793
1794
1795bool FOOTPRINT::HitTestAccurate( const VECTOR2I& aPosition, int aAccuracy ) const
1796{
1797 return GetBoundingHull().Collide( aPosition, aAccuracy );
1798}
1799
1800
1801bool FOOTPRINT::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1802{
1803 BOX2I arect = aRect;
1804 arect.Inflate( aAccuracy );
1805
1806 if( aContained )
1807 {
1808 return arect.Contains( GetBoundingBox( false ) );
1809 }
1810 else
1811 {
1812 // If the rect does not intersect the bounding box, skip any tests
1813 if( !aRect.Intersects( GetBoundingBox( false ) ) )
1814 return false;
1815
1816 // If there are no pads, zones, or drawings, allow intersection with text
1817 if( m_pads.empty() && m_zones.empty() && m_drawings.empty() )
1818 return GetBoundingBox( true ).Intersects( arect );
1819
1820 // Determine if any elements in the FOOTPRINT intersect the rect
1821 for( PAD* pad : m_pads )
1822 {
1823 if( pad->HitTest( arect, false, 0 ) )
1824 return true;
1825 }
1826
1827 for( ZONE* zone : m_zones )
1828 {
1829 if( zone->HitTest( arect, false, 0 ) )
1830 return true;
1831 }
1832
1833 // PCB fields are selectable on their own, so they don't get tested
1834
1835 for( BOARD_ITEM* item : m_drawings )
1836 {
1837 // Text items are selectable on their own, and are therefore excluded from this
1838 // test. TextBox items are NOT selectable on their own, and so MUST be included
1839 // here. Bitmaps aren't selectable since they aren't displayed.
1840 if( item->Type() != PCB_TEXT_T && item->HitTest( arect, false, 0 ) )
1841 return true;
1842 }
1843
1844 // Groups are not hit-tested; only their members
1845
1846 // No items were hit
1847 return false;
1848 }
1849}
1850
1851
1852PAD* FOOTPRINT::FindPadByNumber( const wxString& aPadNumber, PAD* aSearchAfterMe ) const
1853{
1854 bool can_select = aSearchAfterMe ? false : true;
1855
1856 for( PAD* pad : m_pads )
1857 {
1858 if( !can_select && pad == aSearchAfterMe )
1859 {
1860 can_select = true;
1861 continue;
1862 }
1863
1864 if( can_select && pad->GetNumber() == aPadNumber )
1865 return pad;
1866 }
1867
1868 return nullptr;
1869}
1870
1871
1872PAD* FOOTPRINT::GetPad( const VECTOR2I& aPosition, LSET aLayerMask )
1873{
1874 for( PAD* pad : m_pads )
1875 {
1876 // ... and on the correct layer.
1877 if( !( pad->GetLayerSet() & aLayerMask ).any() )
1878 continue;
1879
1880 if( pad->HitTest( aPosition ) )
1881 return pad;
1882 }
1883
1884 return nullptr;
1885}
1886
1887
1888std::vector<const PAD*> FOOTPRINT::GetPads( const wxString& aPadNumber, const PAD* aIgnore ) const
1889{
1890 std::vector<const PAD*> retv;
1891
1892 for( const PAD* pad : m_pads )
1893 {
1894 if( ( aIgnore && aIgnore == pad ) || ( pad->GetNumber() != aPadNumber ) )
1895 continue;
1896
1897 retv.push_back( pad );
1898 }
1899
1900 return retv;
1901}
1902
1903
1904unsigned FOOTPRINT::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
1905{
1906 if( aIncludeNPTH )
1907 return m_pads.size();
1908
1909 unsigned cnt = 0;
1910
1911 for( PAD* pad : m_pads )
1912 {
1913 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1914 continue;
1915
1916 cnt++;
1917 }
1918
1919 return cnt;
1920}
1921
1922
1923std::set<wxString> FOOTPRINT::GetUniquePadNumbers( INCLUDE_NPTH_T aIncludeNPTH ) const
1924{
1925 std::set<wxString> usedNumbers;
1926
1927 // Create a set of used pad numbers
1928 for( PAD* pad : m_pads )
1929 {
1930 // Skip pads not on copper layers (used to build complex
1931 // solder paste shapes for instance)
1932 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
1933 continue;
1934
1935 // Skip pads with no name, because they are usually "mechanical"
1936 // pads, not "electrical" pads
1937 if( pad->GetNumber().IsEmpty() )
1938 continue;
1939
1940 if( !aIncludeNPTH )
1941 {
1942 // skip NPTH
1943 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1944 continue;
1945 }
1946
1947 usedNumbers.insert( pad->GetNumber() );
1948 }
1949
1950 return usedNumbers;
1951}
1952
1953
1954unsigned FOOTPRINT::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
1955{
1956 return GetUniquePadNumbers( aIncludeNPTH ).size();
1957}
1958
1959
1961{
1962 if( nullptr == a3DModel )
1963 return;
1964
1965 if( !a3DModel->m_Filename.empty() )
1966 m_3D_Drawings.push_back( *a3DModel );
1967}
1968
1969
1970// see footprint.h
1971INSPECT_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData,
1972 const std::vector<KICAD_T>& aScanTypes )
1973{
1974#if 0 && defined(DEBUG)
1975 std::cout << GetClass().mb_str() << ' ';
1976#endif
1977
1978 bool drawingsScanned = false;
1979
1980 for( KICAD_T scanType : aScanTypes )
1981 {
1982 switch( scanType )
1983 {
1984 case PCB_FOOTPRINT_T:
1985 if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
1986 return INSPECT_RESULT::QUIT;
1987
1988 break;
1989
1990 case PCB_PAD_T:
1991 if( IterateForward<PAD*>( m_pads, inspector, testData, { scanType } )
1992 == INSPECT_RESULT::QUIT )
1993 {
1994 return INSPECT_RESULT::QUIT;
1995 }
1996
1997 break;
1998
1999 case PCB_ZONE_T:
2000 if( IterateForward<ZONE*>( m_zones, inspector, testData, { scanType } )
2001 == INSPECT_RESULT::QUIT )
2002 {
2003 return INSPECT_RESULT::QUIT;
2004 }
2005
2006 break;
2007
2008 case PCB_FIELD_T:
2009 if( IterateForward<PCB_FIELD*>( m_fields, inspector, testData, { scanType } )
2010 == INSPECT_RESULT::QUIT )
2011 {
2012 return INSPECT_RESULT::QUIT;
2013 }
2014
2015 break;
2016
2017 case PCB_TEXT_T:
2018 case PCB_DIM_ALIGNED_T:
2019 case PCB_DIM_LEADER_T:
2020 case PCB_DIM_CENTER_T:
2021 case PCB_DIM_RADIAL_T:
2023 case PCB_SHAPE_T:
2024 case PCB_TEXTBOX_T:
2025 case PCB_TABLE_T:
2026 case PCB_TABLECELL_T:
2027 if( !drawingsScanned )
2028 {
2029 if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, aScanTypes )
2030 == INSPECT_RESULT::QUIT )
2031 {
2032 return INSPECT_RESULT::QUIT;
2033 }
2034
2035 drawingsScanned = true;
2036 }
2037
2038 break;
2039
2040 case PCB_GROUP_T:
2041 if( IterateForward<PCB_GROUP*>( m_groups, inspector, testData, { scanType } )
2042 == INSPECT_RESULT::QUIT )
2043 {
2044 return INSPECT_RESULT::QUIT;
2045 }
2046
2047 break;
2048
2049 default:
2050 break;
2051 }
2052 }
2053
2054 return INSPECT_RESULT::CONTINUE;
2055}
2056
2057
2058wxString FOOTPRINT::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
2059{
2060 wxString reference = GetReference();
2061
2062 if( reference.IsEmpty() )
2063 reference = _( "<no reference designator>" );
2064
2065 return wxString::Format( _( "Footprint %s" ), reference );
2066}
2067
2068
2070{
2071 return BITMAPS::module;
2072}
2073
2074
2076{
2077 return new FOOTPRINT( *this );
2078}
2079
2080
2081void FOOTPRINT::RunOnChildren( const std::function<void ( BOARD_ITEM* )>& aFunction ) const
2082{
2083 try
2084 {
2085 for( PCB_FIELD* field : m_fields )
2086 aFunction( field );
2087
2088 for( PAD* pad : m_pads )
2089 aFunction( pad );
2090
2091 for( ZONE* zone : m_zones )
2092 aFunction( zone );
2093
2094 for( PCB_GROUP* group : m_groups )
2095 aFunction( group );
2096
2097 for( BOARD_ITEM* drawing : m_drawings )
2098 aFunction( drawing );
2099 }
2100 catch( std::bad_function_call& )
2101 {
2102 wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnChildren" ) );
2103 }
2104}
2105
2106
2107void FOOTPRINT::RunOnDescendants( const std::function<void( BOARD_ITEM* )>& aFunction,
2108 int aDepth ) const
2109{
2110 // Avoid freezes with infinite recursion
2111 if( aDepth > 20 )
2112 return;
2113
2114 try
2115 {
2116 for( PCB_FIELD* field : m_fields )
2117 aFunction( field );
2118
2119 for( PAD* pad : m_pads )
2120 aFunction( pad );
2121
2122 for( ZONE* zone : m_zones )
2123 aFunction( zone );
2124
2125 for( PCB_GROUP* group : m_groups )
2126 {
2127 aFunction( group );
2128 group->RunOnDescendants( aFunction, aDepth + 1 );
2129 }
2130
2131 for( BOARD_ITEM* drawing : m_drawings )
2132 {
2133 aFunction( drawing );
2134 drawing->RunOnDescendants( aFunction, aDepth + 1 );
2135 }
2136 }
2137 catch( std::bad_function_call& )
2138 {
2139 wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnDescendants" ) );
2140 }
2141}
2142
2143
2144std::vector<int> FOOTPRINT::ViewGetLayers() const
2145{
2146 std::vector<int> layers;
2147
2148 layers.reserve( 6 );
2149 layers.push_back( LAYER_ANCHOR );
2150
2151 switch( m_layer )
2152 {
2153 default:
2154 wxASSERT_MSG( false, wxT( "Illegal layer" ) ); // do you really have footprints placed
2155 // on other layers?
2157
2158 case F_Cu:
2159 layers.push_back( LAYER_FOOTPRINTS_FR );
2160 break;
2161
2162 case B_Cu:
2163 layers.push_back( LAYER_FOOTPRINTS_BK );
2164 break;
2165 }
2166
2167 if( IsLocked() )
2168 layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
2169
2170 if( IsConflicting() )
2171 layers.push_back( LAYER_CONFLICTS_SHADOW );
2172
2173 // If there are no pads, and only drawings on a silkscreen layer, then report the silkscreen
2174 // layer as well so that the component can be edited with the silkscreen layer
2175 bool f_silk = false, b_silk = false, non_silk = false;
2176
2177 for( BOARD_ITEM* item : m_drawings )
2178 {
2179 if( item->GetLayer() == F_SilkS )
2180 f_silk = true;
2181 else if( item->GetLayer() == B_SilkS )
2182 b_silk = true;
2183 else
2184 non_silk = true;
2185 }
2186
2187 if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
2188 {
2189 if( f_silk )
2190 layers.push_back( F_SilkS );
2191
2192 if( b_silk )
2193 layers.push_back( B_SilkS );
2194 }
2195
2196 return layers;
2197}
2198
2199
2200double FOOTPRINT::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
2201{
2202 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2203 {
2204 // The locked shadow shape is shown only if the footprint itself is visible
2205 if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
2206 return 0.0;
2207
2208 if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
2209 return 0.0;
2210
2211 return std::numeric_limits<double>::max();
2212 }
2213
2214 if( aLayer == LAYER_CONFLICTS_SHADOW && IsConflicting() )
2215 {
2216 // The locked shadow shape is shown only if the footprint itself is visible
2217 if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
2218 return 0.0;
2219
2220 if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
2221 return 0.0;
2222
2223 return std::numeric_limits<double>::max();
2224 }
2225
2226 int layer = ( m_layer == F_Cu ) ? LAYER_FOOTPRINTS_FR :
2228
2229 // Currently this is only pertinent for the anchor layer; everything else is drawn from the
2230 // children.
2231 // The "good" value is experimentally chosen.
2232 #define MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY 1.5
2233
2234 if( aView->IsLayerVisible( layer ) )
2236
2237 return std::numeric_limits<double>::max();
2238}
2239
2240
2242{
2243 BOX2I area = GetBoundingBox( true );
2244
2245 // Inflate in case clearance lines are drawn around pads, etc.
2246 if( const BOARD* board = GetBoard() )
2247 {
2248 int biggest_clearance = board->GetMaxClearanceValue();
2249 area.Inflate( biggest_clearance );
2250 }
2251
2252 return area;
2253}
2254
2255
2256bool FOOTPRINT::IsLibNameValid( const wxString & aName )
2257{
2258 const wxChar * invalids = StringLibNameInvalidChars( false );
2259
2260 if( aName.find_first_of( invalids ) != std::string::npos )
2261 return false;
2262
2263 return true;
2264}
2265
2266
2267const wxChar* FOOTPRINT::StringLibNameInvalidChars( bool aUserReadable )
2268{
2269 // This list of characters is also duplicated in validators.cpp and
2270 // lib_id.cpp
2271 // TODO: Unify forbidden character lists - Warning, invalid filename characters are not the same
2272 // as invalid LIB_ID characters. We will need to separate the FP filenames from FP names before this
2273 // can be unified
2274 static const wxChar invalidChars[] = wxT("%$<>\t\n\r\"\\/:");
2275 static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
2276
2277 if( aUserReadable )
2278 return invalidCharsReadable;
2279 else
2280 return invalidChars;
2281}
2282
2283
2284void FOOTPRINT::Move( const VECTOR2I& aMoveVector )
2285{
2286 if( aMoveVector.x == 0 && aMoveVector.y == 0 )
2287 return;
2288
2289 VECTOR2I newpos = m_pos + aMoveVector;
2290 SetPosition( newpos );
2291}
2292
2293
2294void FOOTPRINT::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
2295{
2296 if( aAngle == ANGLE_0 )
2297 return;
2298
2299 EDA_ANGLE orientation = GetOrientation();
2300 EDA_ANGLE newOrientation = orientation + aAngle;
2301 VECTOR2I newpos = m_pos;
2302 RotatePoint( newpos, aRotCentre, aAngle );
2303 SetPosition( newpos );
2304 SetOrientation( newOrientation );
2305
2306 for( PCB_FIELD* field : m_fields )
2307 field->KeepUpright();
2308
2309 for( BOARD_ITEM* item : m_drawings )
2310 {
2311 if( item->Type() == PCB_TEXT_T )
2312 static_cast<PCB_TEXT*>( item )->KeepUpright();
2313 }
2314
2318}
2319
2320
2322{
2323 wxASSERT( aLayer == F_Cu || aLayer == B_Cu );
2324
2325 if( aLayer != GetLayer() )
2326 Flip( GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
2327}
2328
2329
2330void FOOTPRINT::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
2331{
2332 // Move footprint to its final position:
2333 VECTOR2I finalPos = m_pos;
2334
2335 // Now Flip the footprint.
2336 // Flipping a footprint is a specific transform: it is not mirrored like a text.
2337 // We have to change the side, and ensure the footprint rotation is modified according to the
2338 // transform, because this parameter is used in pick and place files, and when updating the
2339 // footprint from library.
2340 // When flipped around the X axis (Y coordinates changed) orientation is negated
2341 // When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
2342 // Because it is specific to a footprint, we flip around the X axis, and after rotate 180 deg
2343
2344 MIRROR( finalPos.y, aCentre.y );
2345
2346 SetPosition( finalPos );
2347
2348 // Flip layer
2350
2351 // Calculate the new orientation, and then clear it for pad flipping.
2352 EDA_ANGLE newOrientation = -m_orient;
2353 newOrientation.Normalize180();
2354 m_orient = ANGLE_0;
2355
2356 // Mirror fields to other side of board.
2357 for( PCB_FIELD* field : m_fields )
2358 field->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2359
2360 // Mirror pads to other side of board.
2361 for( PAD* pad : m_pads )
2362 pad->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2363
2364 // Now set the new orientation.
2365 m_orient = newOrientation;
2366
2367 // Mirror zones to other side of board.
2368 for( ZONE* zone : m_zones )
2369 zone->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2370
2371 // Reverse mirror footprint graphics and texts.
2372 for( BOARD_ITEM* item : m_drawings )
2373 item->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2374
2375 // Now rotate 180 deg if required
2376 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
2377 Rotate( aCentre, ANGLE_180 );
2378
2381
2382 m_cachedHull.Mirror( m_pos, aFlipDirection );
2383
2385}
2386
2387
2389{
2390 VECTOR2I delta = aPos - m_pos;
2391
2392 m_pos += delta;
2393
2394 for( PCB_FIELD* field : m_fields )
2395 field->EDA_TEXT::Offset( delta );
2396
2397 for( PAD* pad : m_pads )
2398 pad->SetPosition( pad->GetPosition() + delta );
2399
2400 for( ZONE* zone : m_zones )
2401 zone->Move( delta );
2402
2403 for( BOARD_ITEM* item : m_drawings )
2404 item->Move( delta );
2405
2411}
2412
2413
2414void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
2415{
2416 /*
2417 * Move the reference point of the footprint
2418 * the footprints elements (pads, outlines, edges .. ) are moved
2419 * but:
2420 * - the footprint position is not modified.
2421 * - the relative (local) coordinates of these items are modified
2422 * - Draw coordinates are updated
2423 */
2424
2425 // Update (move) the relative coordinates relative to the new anchor point.
2426 VECTOR2I moveVector = aMoveVector;
2427 RotatePoint( moveVector, -GetOrientation() );
2428
2429 // Update field local coordinates
2430 for( PCB_FIELD* field : m_fields )
2431 field->Move( moveVector );
2432
2433 // Update the pad local coordinates.
2434 for( PAD* pad : m_pads )
2435 pad->Move( moveVector );
2436
2437 // Update the draw element coordinates.
2438 for( BOARD_ITEM* item : GraphicalItems() )
2439 item->Move( moveVector );
2440
2441 // Update the keepout zones
2442 for( ZONE* zone : Zones() )
2443 zone->Move( moveVector );
2444
2445 // Update the 3D models
2446 for( FP_3DMODEL& model : Models() )
2447 {
2448 model.m_Offset.x += pcbIUScale.IUTomm( moveVector.x );
2449 model.m_Offset.y -= pcbIUScale.IUTomm( moveVector.y );
2450 }
2451
2452 m_cachedBoundingBox.Move( moveVector );
2453 m_cachedTextExcludedBBox.Move( moveVector );
2454 m_cachedHull.Move( moveVector );
2455}
2456
2457
2458void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
2459{
2460 EDA_ANGLE angleChange = aNewAngle - m_orient; // change in rotation
2461
2462 m_orient = aNewAngle;
2464
2465 for( PCB_FIELD* field : m_fields )
2466 field->Rotate( GetPosition(), angleChange );
2467
2468 for( PAD* pad : m_pads )
2469 pad->Rotate( GetPosition(), angleChange );
2470
2471 for( ZONE* zone : m_zones )
2472 zone->Rotate( GetPosition(), angleChange );
2473
2474 for( BOARD_ITEM* item : m_drawings )
2475 item->Rotate( GetPosition(), angleChange );
2476
2479
2480 m_cachedHull.Rotate( angleChange, GetPosition() );
2481}
2482
2483
2485{
2486 FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate() );
2487
2488 dupe->RunOnDescendants( [&]( BOARD_ITEM* child )
2489 {
2490 const_cast<KIID&>( child->m_Uuid ) = KIID();
2491 });
2492
2493 return dupe;
2494}
2495
2496
2497BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootprint )
2498{
2499 BOARD_ITEM* new_item = nullptr;
2500
2501 switch( aItem->Type() )
2502 {
2503 case PCB_PAD_T:
2504 {
2505 PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
2506 const_cast<KIID&>( new_pad->m_Uuid ) = KIID();
2507
2508 if( aAddToFootprint )
2509 m_pads.push_back( new_pad );
2510
2511 new_item = new_pad;
2512 break;
2513 }
2514
2515 case PCB_ZONE_T:
2516 {
2517 ZONE* new_zone = new ZONE( *static_cast<const ZONE*>( aItem ) );
2518 const_cast<KIID&>( new_zone->m_Uuid ) = KIID();
2519
2520 if( aAddToFootprint )
2521 m_zones.push_back( new_zone );
2522
2523 new_item = new_zone;
2524 break;
2525 }
2526
2527 case PCB_FIELD_T:
2528 case PCB_TEXT_T:
2529 {
2530 PCB_TEXT* new_text = new PCB_TEXT( *static_cast<const PCB_TEXT*>( aItem ) );
2531 const_cast<KIID&>( new_text->m_Uuid ) = KIID();
2532
2533 if( aItem->Type() == PCB_FIELD_T )
2534 {
2535 switch( static_cast<const PCB_FIELD*>( aItem )->GetId() )
2536 {
2537 case REFERENCE_FIELD: new_text->SetText( wxT( "${REFERENCE}" ) ); break;
2538
2539 case VALUE_FIELD: new_text->SetText( wxT( "${VALUE}" ) ); break;
2540
2541 case DATASHEET_FIELD: new_text->SetText( wxT( "${DATASHEET}" ) ); break;
2542 }
2543 }
2544
2545 if( aAddToFootprint )
2546 Add( new_text );
2547
2548 new_item = new_text;
2549 break;
2550 }
2551
2552 case PCB_SHAPE_T:
2553 {
2554 PCB_SHAPE* new_shape = new PCB_SHAPE( *static_cast<const PCB_SHAPE*>( aItem ) );
2555 const_cast<KIID&>( new_shape->m_Uuid ) = KIID();
2556
2557 if( aAddToFootprint )
2558 Add( new_shape );
2559
2560 new_item = new_shape;
2561 break;
2562 }
2563
2564 case PCB_TEXTBOX_T:
2565 {
2566 PCB_TEXTBOX* new_textbox = new PCB_TEXTBOX( *static_cast<const PCB_TEXTBOX*>( aItem ) );
2567 const_cast<KIID&>( new_textbox->m_Uuid ) = KIID();
2568
2569 if( aAddToFootprint )
2570 Add( new_textbox );
2571
2572 new_item = new_textbox;
2573 break;
2574 }
2575
2576 case PCB_DIM_ALIGNED_T:
2577 case PCB_DIM_LEADER_T:
2578 case PCB_DIM_CENTER_T:
2579 case PCB_DIM_RADIAL_T:
2581 {
2582 PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate() );
2583
2584 if( aAddToFootprint )
2585 Add( dimension );
2586
2587 new_item = dimension;
2588 break;
2589 }
2590
2591 case PCB_GROUP_T:
2592 {
2593 PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate();
2594
2595 if( aAddToFootprint )
2596 {
2597 group->RunOnDescendants(
2598 [&]( BOARD_ITEM* aCurrItem )
2599 {
2600 Add( aCurrItem );
2601 } );
2602
2603 Add( new_item );
2604 }
2605
2606 new_item = group;
2607 break;
2608 }
2609
2610 case PCB_FOOTPRINT_T:
2611 // Ignore the footprint itself
2612 break;
2613
2614 default:
2615 // Un-handled item for duplication
2616 wxFAIL_MSG( wxT( "Duplication not supported for items of class " ) + aItem->GetClass() );
2617 break;
2618 }
2619
2620 return new_item;
2621}
2622
2623
2624wxString FOOTPRINT::GetNextPadNumber( const wxString& aLastPadNumber ) const
2625{
2626 std::set<wxString> usedNumbers;
2627
2628 // Create a set of used pad numbers
2629 for( PAD* pad : m_pads )
2630 usedNumbers.insert( pad->GetNumber() );
2631
2632 // Pad numbers aren't technically reference designators, but the formatting is close enough
2633 // for these to give us what we need.
2634 wxString prefix = UTIL::GetRefDesPrefix( aLastPadNumber );
2635 int num = GetTrailingInt( aLastPadNumber );
2636
2637 while( usedNumbers.count( wxString::Format( wxT( "%s%d" ), prefix, num ) ) )
2638 num++;
2639
2640 return wxString::Format( wxT( "%s%d" ), prefix, num );
2641}
2642
2643
2645{
2646 // Auto-position reference and value
2647 BOX2I bbox = GetBoundingBox( false );
2648 bbox.Inflate( pcbIUScale.mmToIU( 0.2 ) ); // Gap between graphics and text
2649
2650 if( Reference().GetPosition() == VECTOR2I( 0, 0 ) )
2651 {
2655
2656 Reference().SetX( bbox.GetCenter().x );
2657 Reference().SetY( bbox.GetTop() - Reference().GetTextSize().y / 2 );
2658 }
2659
2660 if( Value().GetPosition() == VECTOR2I( 0, 0 ) )
2661 {
2665
2666 Value().SetX( bbox.GetCenter().x );
2667 Value().SetY( bbox.GetBottom() + Value().GetTextSize().y / 2 );
2668 }
2669}
2670
2671
2673{
2674 const wxString& refdes = GetReference();
2675
2676 SetReference( wxString::Format( wxT( "%s%i" ),
2677 UTIL::GetRefDesPrefix( refdes ),
2678 GetTrailingInt( refdes ) + aDelta ) );
2679}
2680
2681
2682// Calculate the area of a PolySet, polygons with hole are allowed.
2683static double polygonArea( SHAPE_POLY_SET& aPolySet )
2684{
2685 // Ensure all outlines are closed, before calculating the SHAPE_POLY_SET area
2686 for( int ii = 0; ii < aPolySet.OutlineCount(); ii++ )
2687 {
2688 SHAPE_LINE_CHAIN& outline = aPolySet.Outline( ii );
2689 outline.SetClosed( true );
2690
2691 for( int jj = 0; jj < aPolySet.HoleCount( ii ); jj++ )
2692 aPolySet.Hole( ii, jj ).SetClosed( true );
2693 }
2694
2695 return aPolySet.Area();
2696}
2697
2698
2699double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLECTOR& aCollector )
2700{
2701 int textMargin = aCollector.GetGuide()->Accuracy();
2702 SHAPE_POLY_SET poly;
2703
2704 if( aItem->Type() == PCB_MARKER_T )
2705 {
2706 const PCB_MARKER* marker = static_cast<const PCB_MARKER*>( aItem );
2707 SHAPE_LINE_CHAIN markerShape;
2708
2709 marker->ShapeToPolygon( markerShape );
2710 return markerShape.Area();
2711 }
2712 else if( aItem->Type() == PCB_GROUP_T || aItem->Type() == PCB_GENERATOR_T )
2713 {
2714 double combinedArea = 0.0;
2715
2716 for( BOARD_ITEM* member : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
2717 combinedArea += GetCoverageArea( member, aCollector );
2718
2719 return combinedArea;
2720 }
2721 if( aItem->Type() == PCB_FOOTPRINT_T )
2722 {
2723 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
2724
2725 poly = footprint->GetBoundingHull();
2726 }
2727 else if( aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T )
2728 {
2729 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aItem );
2730
2731 text->TransformTextToPolySet( poly, textMargin, ARC_LOW_DEF, ERROR_INSIDE );
2732 }
2733 else if( aItem->Type() == PCB_TEXTBOX_T )
2734 {
2735 const PCB_TEXTBOX* tb = static_cast<const PCB_TEXTBOX*>( aItem );
2736
2737 tb->TransformTextToPolySet( poly, textMargin, ARC_LOW_DEF, ERROR_INSIDE );
2738 }
2739 else if( aItem->Type() == PCB_SHAPE_T )
2740 {
2741 // Approximate "linear" shapes with just their width squared, as we don't want to consider
2742 // a linear shape as being much bigger than another for purposes of selection filtering
2743 // just because it happens to be really long.
2744
2745 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
2746
2747 switch( shape->GetShape() )
2748 {
2749 case SHAPE_T::SEGMENT:
2750 case SHAPE_T::ARC:
2751 case SHAPE_T::BEZIER:
2752 return shape->GetWidth() * shape->GetWidth();
2753
2754 case SHAPE_T::RECTANGLE:
2755 case SHAPE_T::CIRCLE:
2756 case SHAPE_T::POLY:
2757 {
2758 if( !shape->IsFilled() )
2759 return shape->GetWidth() * shape->GetWidth();
2760
2762 }
2763
2764 default:
2766 }
2767 }
2768 else if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
2769 {
2770 double width = static_cast<const PCB_TRACK*>( aItem )->GetWidth();
2771 return width * width;
2772 }
2773 else if( aItem->Type() == PCB_PAD_T )
2774 {
2775 static_cast<const PAD*>( aItem )->Padstack().ForEachUniqueLayer(
2776 [&]( PCB_LAYER_ID aLayer )
2777 {
2778 SHAPE_POLY_SET padPoly;
2779 aItem->TransformShapeToPolygon( padPoly, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2780 poly.BooleanAdd( padPoly, SHAPE_POLY_SET::PM_FAST );
2781 } );
2782 }
2783 else
2784 {
2786 }
2787
2788 return polygonArea( poly );
2789}
2790
2791
2792double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
2793{
2794 int textMargin = aCollector.GetGuide()->Accuracy();
2795
2796 SHAPE_POLY_SET footprintRegion( GetBoundingHull() );
2797 SHAPE_POLY_SET coveredRegion;
2798
2800
2801 TransformFPShapesToPolySet( coveredRegion, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF,
2803 true, /* include text */
2804 false, /* include shapes */
2805 false /* include private items */ );
2806
2807 for( int i = 0; i < aCollector.GetCount(); ++i )
2808 {
2809 const BOARD_ITEM* item = aCollector[i];
2810
2811 switch( item->Type() )
2812 {
2813 case PCB_FIELD_T:
2814 case PCB_TEXT_T:
2815 case PCB_TEXTBOX_T:
2816 case PCB_SHAPE_T:
2817 case PCB_TRACE_T:
2818 case PCB_ARC_T:
2819 case PCB_VIA_T:
2820 if( item->GetParent() != this )
2821 {
2822 item->TransformShapeToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
2823 ERROR_OUTSIDE );
2824 }
2825 break;
2826
2827 case PCB_FOOTPRINT_T:
2828 if( item != this )
2829 {
2830 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( item );
2831 coveredRegion.AddOutline( footprint->GetBoundingHull().Outline( 0 ) );
2832 }
2833 break;
2834
2835 default:
2836 break;
2837 }
2838 }
2839
2840 coveredRegion.BooleanIntersection( footprintRegion, SHAPE_POLY_SET::PM_FAST );
2841
2842 double footprintRegionArea = polygonArea( footprintRegion );
2843 double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion );
2844 double coveredArea = footprintRegionArea - uncoveredRegionArea;
2845 double ratio = ( coveredArea / footprintRegionArea );
2846
2847 // Test for negative ratio (should not occur).
2848 // better to be conservative (this will result in the disambiguate dialog)
2849 if( ratio < 0.0 )
2850 return 1.0;
2851
2852 return std::min( ratio, 1.0 );
2853}
2854
2855
2856std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
2857{
2858 std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
2859
2860 // There are several possible interpretations here:
2861 // 1) the bounding box (without or without invisible items)
2862 // 2) just the pads and "edges" (ie: non-text graphic items)
2863 // 3) the courtyard
2864
2865 // We'll go with (2) for now, unless the caller is clearly looking for (3)
2866
2867 if( aLayer == F_CrtYd || aLayer == B_CrtYd )
2868 {
2869 const SHAPE_POLY_SET& courtyard = GetCourtyard( aLayer );
2870
2871 if( courtyard.OutlineCount() == 0 ) // malformed/empty polygon
2872 return shape;
2873
2874 shape->AddShape( new SHAPE_SIMPLE( courtyard.COutline( 0 ) ) );
2875 }
2876 else
2877 {
2878 for( PAD* pad : Pads() )
2879 shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
2880
2881 for( BOARD_ITEM* item : GraphicalItems() )
2882 {
2883 if( item->Type() == PCB_SHAPE_T )
2884 shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
2885 }
2886 }
2887
2888 return shape;
2889}
2890
2891
2893{
2894 std::lock_guard<std::mutex> lock( m_courtyard_cache_mutex );
2895
2898 {
2899 const_cast<FOOTPRINT*>( this )->BuildCourtyardCaches();
2900 }
2901
2902 return GetCachedCourtyard( aLayer );
2903}
2904
2905
2907{
2908 if( IsBackLayer( aLayer ) )
2910 else
2912}
2913
2914
2916{
2920
2921 // Build the courtyard area from graphic items on the courtyard.
2922 // Only PCB_SHAPE_T have meaning, graphic texts are ignored.
2923 // Collect items:
2924 std::vector<PCB_SHAPE*> list_front;
2925 std::vector<PCB_SHAPE*> list_back;
2926 std::map<int, int> front_width_histogram;
2927 std::map<int, int> back_width_histogram;
2928
2929 for( BOARD_ITEM* item : GraphicalItems() )
2930 {
2931 if( item->GetLayer() == B_CrtYd && item->Type() == PCB_SHAPE_T )
2932 {
2933 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2934 list_back.push_back( shape );
2935 back_width_histogram[ shape->GetStroke().GetWidth() ]++;
2936 }
2937
2938 if( item->GetLayer() == F_CrtYd && item->Type() == PCB_SHAPE_T )
2939 {
2940 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2941 list_front.push_back( shape );
2942 front_width_histogram[ shape->GetStroke().GetWidth() ]++;
2943 }
2944 }
2945
2946 if( !list_front.size() && !list_back.size() )
2947 return;
2948
2949 int maxError = pcbIUScale.mmToIU( 0.005 ); // max error for polygonization
2950 int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
2951
2952 if( ConvertOutlineToPolygon( list_front, m_courtyard_cache_front, maxError, chainingEpsilon,
2953 true, aErrorHandler ) )
2954 {
2955 int width = 0;
2956
2957 // Touching courtyards, or courtyards -at- the clearance distance are legal.
2958 // Use maxError here because that is the allowed deviation when transforming arcs/circles to
2959 // polygons.
2960 m_courtyard_cache_front.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
2961
2963 auto max = std::max_element( front_width_histogram.begin(), front_width_histogram.end(),
2964 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
2965 {
2966 return a.second < b.second;
2967 } );
2968
2969 if( max != front_width_histogram.end() )
2970 width = max->first;
2971
2972 if( width == 0 )
2974
2977 }
2978 else
2979 {
2981 }
2982
2983 if( ConvertOutlineToPolygon( list_back, m_courtyard_cache_back, maxError, chainingEpsilon, true,
2984 aErrorHandler ) )
2985 {
2986 int width = 0;
2987
2988 // Touching courtyards, or courtyards -at- the clearance distance are legal.
2989 m_courtyard_cache_back.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
2990
2992 auto max = std::max_element( back_width_histogram.begin(), back_width_histogram.end(),
2993 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
2994 {
2995 return a.second < b.second;
2996 } );
2997
2998 if( max != back_width_histogram.end() )
2999 width = max->first;
3000
3001 if( width == 0 )
3003
3006 }
3007 else
3008 {
3010 }
3011
3014}
3015
3016
3018{
3019 m_netTieCache.clear();
3020 std::map<wxString, int> map = MapPadNumbersToNetTieGroups();
3021 std::map<PCB_LAYER_ID, std::vector<PCB_SHAPE*>> layer_shapes;
3022
3023 std::for_each( m_drawings.begin(), m_drawings.end(),
3024 [&]( BOARD_ITEM* item )
3025 {
3026 if( item->Type() != PCB_SHAPE_T )
3027 return;
3028
3029 for( PCB_LAYER_ID layer : item->GetLayerSet() )
3030 layer_shapes[layer].push_back( static_cast<PCB_SHAPE*>( item ) );
3031 } );
3032
3033 for( size_t ii = 0; ii < m_pads.size(); ++ii )
3034 {
3035 PAD* pad = m_pads[ ii ];
3036 bool has_nettie = false;
3037
3038 auto it = map.find( pad->GetNumber() );
3039
3040 if( it == map.end() || it->second < 0 )
3041 continue;
3042
3043 for( size_t jj = ii + 1; jj < m_pads.size(); ++jj )
3044 {
3045 PAD* other = m_pads[ jj ];
3046
3047 auto it2 = map.find( other->GetNumber() );
3048
3049 if( it2 == map.end() || it2->second < 0 )
3050 continue;
3051
3052 if( it2->second == it->second )
3053 {
3054 m_netTieCache[pad].insert( pad->GetNetCode() );
3055 m_netTieCache[pad].insert( other->GetNetCode() );
3056 m_netTieCache[other].insert( other->GetNetCode() );
3057 m_netTieCache[other].insert( pad->GetNetCode() );
3058 has_nettie = true;
3059 }
3060 }
3061
3062 if( !has_nettie )
3063 continue;
3064
3065 for( auto& [ layer, shapes ] : layer_shapes )
3066 {
3067 auto pad_shape = pad->GetEffectiveShape( layer );
3068
3069 for( auto other_shape : shapes )
3070 {
3071 auto shape = other_shape->GetEffectiveShape( layer );
3072
3073 if( pad_shape->Collide( shape.get() ) )
3074 {
3075 std::set<int>& nettie = m_netTieCache[pad];
3076 m_netTieCache[other_shape].insert( nettie.begin(), nettie.end() );
3077 }
3078 }
3079 }
3080 }
3081}
3082
3083
3084std::map<wxString, int> FOOTPRINT::MapPadNumbersToNetTieGroups() const
3085{
3086 std::map<wxString, int> padNumberToGroupIdxMap;
3087
3088 for( const PAD* pad : m_pads )
3089 padNumberToGroupIdxMap[ pad->GetNumber() ] = -1;
3090
3091 auto processPad =
3092 [&]( wxString aPad, int aGroup )
3093 {
3094 aPad.Trim( true ).Trim( false );
3095
3096 if( !aPad.IsEmpty() )
3097 padNumberToGroupIdxMap[ aPad ] = aGroup;
3098 };
3099
3100 for( int ii = 0; ii < (int) m_netTiePadGroups.size(); ++ii )
3101 {
3102 wxString group( m_netTiePadGroups[ ii ] );
3103 bool esc = false;
3104 wxString pad;
3105
3106 for( wxUniCharRef ch : group )
3107 {
3108 if( esc )
3109 {
3110 esc = false;
3111 pad.Append( ch );
3112 continue;
3113 }
3114
3115 switch( static_cast<unsigned char>( ch ) )
3116 {
3117 case '\\':
3118 esc = true;
3119 break;
3120
3121 case ',':
3122 processPad( pad, ii );
3123 pad.Clear();
3124 break;
3125
3126 default:
3127 pad.Append( ch );
3128 break;
3129 }
3130 }
3131
3132 processPad( pad, ii );
3133 }
3134
3135 return padNumberToGroupIdxMap;
3136}
3137
3138
3139std::vector<PAD*> FOOTPRINT::GetNetTiePads( PAD* aPad ) const
3140{
3141 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
3142 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
3143
3144 std::map<wxString, int> padToNetTieGroupMap = MapPadNumbersToNetTieGroups();
3145 int groupIdx = padToNetTieGroupMap[ aPad->GetNumber() ];
3146 std::vector<PAD*> otherPads;
3147
3148 if( groupIdx >= 0 )
3149 {
3150 for( PAD* pad : m_pads )
3151 {
3152 if( padToNetTieGroupMap[ pad->GetNumber() ] == groupIdx )
3153 otherPads.push_back( pad );
3154 }
3155 }
3156
3157 return otherPads;
3158}
3159
3160
3161void FOOTPRINT::CheckFootprintAttributes( const std::function<void( const wxString& )>& aErrorHandler )
3162{
3163 int likelyAttr = ( GetLikelyAttribute() & ( FP_SMD | FP_THROUGH_HOLE ) );
3164 int setAttr = ( GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
3165
3166 if( setAttr && likelyAttr && setAttr != likelyAttr )
3167 {
3168 wxString msg;
3169
3170 switch( likelyAttr )
3171 {
3172 case FP_THROUGH_HOLE:
3173 msg.Printf( _( "(expected 'Through hole'; actual '%s')" ), GetTypeName() );
3174 break;
3175 case FP_SMD:
3176 msg.Printf( _( "(expected 'SMD'; actual '%s')" ), GetTypeName() );
3177 break;
3178 }
3179
3180 if( aErrorHandler )
3181 (aErrorHandler)( msg );
3182 }
3183}
3184
3185
3187 const std::function<void( const PAD*, int,
3188 const wxString& )>& aErrorHandler )
3189{
3190 if( aErrorHandler == nullptr )
3191 return;
3192
3193 for( PAD* pad: Pads() )
3194 {
3195 pad->CheckPad( aUnitsProvider,
3196 [&]( int errorCode, const wxString& msg )
3197 {
3198 aErrorHandler( pad, errorCode, msg );
3199 } );
3200 }
3201}
3202
3203
3204void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const PAD*,
3205 int aErrorCode,
3206 const VECTOR2I& )>& aErrorHandler )
3207{
3208 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
3209
3210 for( PAD* pad : Pads() )
3211 {
3212 std::vector<PAD*> netTiePads = GetNetTiePads( pad );
3213
3214 for( PAD* other : Pads() )
3215 {
3216 if( other == pad )
3217 continue;
3218
3219 // store canonical order so we don't collide in both directions (a:b and b:a)
3220 PAD* a = pad;
3221 PAD* b = other;
3222
3223 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
3224 std::swap( a, b );
3225
3226 if( checkedPairs.find( { a, b } ) == checkedPairs.end() )
3227 {
3228 checkedPairs[ { a, b } ] = 1;
3229
3230 if( pad->HasDrilledHole() && other->HasDrilledHole() )
3231 {
3232 VECTOR2I pos = pad->GetPosition();
3233
3234 if( pad->GetPosition() == other->GetPosition() )
3235 {
3236 aErrorHandler( pad, other, DRCE_DRILLED_HOLES_COLOCATED, pos );
3237 }
3238 else
3239 {
3240 std::shared_ptr<SHAPE_SEGMENT> holeA = pad->GetEffectiveHoleShape();
3241 std::shared_ptr<SHAPE_SEGMENT> holeB = other->GetEffectiveHoleShape();
3242
3243 if( holeA->Collide( holeB->GetSeg(), 0 ) )
3244 aErrorHandler( pad, other, DRCE_DRILLED_HOLES_TOO_CLOSE, pos );
3245 }
3246 }
3247
3248 if( pad->SameLogicalPadAs( other ) || alg::contains( netTiePads, other ) )
3249 continue;
3250
3251 if( !( ( pad->GetLayerSet() & other->GetLayerSet() ) & LSET::AllCuMask() ).any() )
3252 continue;
3253
3254 if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
3255 {
3256 VECTOR2I pos;
3257
3258 for( PCB_LAYER_ID l : pad->Padstack().RelevantShapeLayers( other->Padstack() ) )
3259 {
3260 SHAPE* padShape = pad->GetEffectiveShape( l ).get();
3261 SHAPE* otherShape = other->GetEffectiveShape( l ).get();
3262
3263 if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
3264 aErrorHandler( pad, other, DRCE_SHORTING_ITEMS, pos );
3265 }
3266 }
3267 }
3268 }
3269 }
3270}
3271
3272
3273void FOOTPRINT::CheckNetTies( const std::function<void( const BOARD_ITEM* aItem,
3274 const BOARD_ITEM* bItem,
3275 const BOARD_ITEM* cItem,
3276 const VECTOR2I& )>& aErrorHandler )
3277{
3278 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
3279 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
3280
3281 std::map<wxString, int> padNumberToGroupIdxMap = MapPadNumbersToNetTieGroups();
3282
3283 // Now collect all the footprint items which are on copper layers
3284
3285 std::vector<BOARD_ITEM*> copperItems;
3286
3287 for( BOARD_ITEM* item : m_drawings )
3288 {
3289 if( item->IsOnCopperLayer() )
3290 copperItems.push_back( item );
3291
3292 item->RunOnDescendants(
3293 [&]( BOARD_ITEM* descendent )
3294 {
3295 if( descendent->IsOnCopperLayer() )
3296 copperItems.push_back( descendent );
3297 } );
3298 }
3299
3300 for( ZONE* zone : m_zones )
3301 {
3302 if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
3303 copperItems.push_back( zone );
3304 }
3305
3306 for( PCB_FIELD* field : m_fields )
3307 {
3308 if( field->IsOnCopperLayer() )
3309 copperItems.push_back( field );
3310 }
3311
3312 for( PCB_LAYER_ID layer : { F_Cu, In1_Cu, B_Cu } )
3313 {
3314 // Next, build a polygon-set for the copper on this layer. We don't really care about
3315 // nets here, we just want to end up with a set of outlines describing the distinct
3316 // copper polygons of the footprint.
3317
3318 SHAPE_POLY_SET copperOutlines;
3319 std::map<int, std::vector<const PAD*>> outlineIdxToPadsMap;
3320
3321 for( BOARD_ITEM* item : copperItems )
3322 {
3323 if( item->IsOnLayer( layer ) )
3324 {
3325 item->TransformShapeToPolygon( copperOutlines, layer, 0, ARC_HIGH_DEF,
3326 ERROR_OUTSIDE );
3327 }
3328 }
3329
3330 copperOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
3331
3332 // Index each pad to the outline in the set that it is part of.
3333
3334 for( const PAD* pad : m_pads )
3335 {
3336 for( int ii = 0; ii < copperOutlines.OutlineCount(); ++ii )
3337 {
3338 if( pad->GetEffectiveShape( layer )->Collide( &copperOutlines.Outline( ii ), 0 ) )
3339 outlineIdxToPadsMap[ ii ].emplace_back( pad );
3340 }
3341 }
3342
3343 // Finally, ensure that each outline which contains multiple pads has all its pads
3344 // listed in an allowed-shorting group.
3345
3346 for( const auto& [ outlineIdx, pads ] : outlineIdxToPadsMap )
3347 {
3348 if( pads.size() > 1 )
3349 {
3350 const PAD* firstPad = pads[0];
3351 int firstGroupIdx = padNumberToGroupIdxMap[ firstPad->GetNumber() ];
3352
3353 for( size_t ii = 1; ii < pads.size(); ++ii )
3354 {
3355 const PAD* thisPad = pads[ii];
3356 int thisGroupIdx = padNumberToGroupIdxMap[ thisPad->GetNumber() ];
3357
3358 if( thisGroupIdx < 0 || thisGroupIdx != firstGroupIdx )
3359 {
3360 BOARD_ITEM* shortingItem = nullptr;
3361 VECTOR2I pos = ( firstPad->GetPosition() + thisPad->GetPosition() ) / 2;
3362
3363 pos = copperOutlines.Outline( outlineIdx ).NearestPoint( pos );
3364
3365 for( BOARD_ITEM* item : copperItems )
3366 {
3367 if( item->HitTest( pos, 1 ) )
3368 {
3369 shortingItem = item;
3370 break;
3371 }
3372 }
3373
3374 if( shortingItem )
3375 aErrorHandler( shortingItem, firstPad, thisPad, pos );
3376 else
3377 aErrorHandler( firstPad, thisPad, nullptr, pos );
3378 }
3379 }
3380 }
3381 }
3382 }
3383}
3384
3385
3386void FOOTPRINT::CheckNetTiePadGroups( const std::function<void( const wxString& )>& aErrorHandler )
3387{
3388 std::set<wxString> padNumbers;
3389 wxString msg;
3390
3391 for( const auto& [ padNumber, _ ] : MapPadNumbersToNetTieGroups() )
3392 {
3393 const PAD* pad = FindPadByNumber( padNumber );
3394
3395 if( !pad )
3396 {
3397 msg.Printf( _( "(net-tie pad group contains unknown pad number %s)" ), padNumber );
3398 aErrorHandler( msg );
3399 }
3400 else if( !padNumbers.insert( pad->GetNumber() ).second )
3401 {
3402 msg.Printf( _( "(pad %s appears in more than one net-tie pad group)" ), padNumber );
3403 aErrorHandler( msg );
3404 }
3405 }
3406}
3407
3408
3410{
3411 wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );
3412
3413 FOOTPRINT* image = static_cast<FOOTPRINT*>( aImage );
3414
3415 std::swap( *this, *image );
3416
3418 [&]( BOARD_ITEM* child )
3419 {
3420 child->SetParent( this );
3421 } );
3422
3423 image->RunOnChildren(
3424 [&]( BOARD_ITEM* child )
3425 {
3426 child->SetParent( image );
3427 } );
3428}
3429
3430
3432{
3433 for( PAD* pad : Pads() )
3434 {
3435 if( pad->GetAttribute() != PAD_ATTRIB::SMD )
3436 return true;
3437 }
3438
3439 return false;
3440}
3441
3442
3443bool FOOTPRINT::operator==( const BOARD_ITEM& aOther ) const
3444{
3445 if( aOther.Type() != PCB_FOOTPRINT_T )
3446 return false;
3447
3448 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
3449
3450 return *this == other;
3451}
3452
3453
3454bool FOOTPRINT::operator==( const FOOTPRINT& aOther ) const
3455{
3456 if( m_pads.size() != aOther.m_pads.size() )
3457 return false;
3458
3459 for( size_t ii = 0; ii < m_pads.size(); ++ii )
3460 {
3461 if( !( *m_pads[ii] == *aOther.m_pads[ii] ) )
3462 return false;
3463 }
3464
3465 if( m_drawings.size() != aOther.m_drawings.size() )
3466 return false;
3467
3468 for( size_t ii = 0; ii < m_drawings.size(); ++ii )
3469 {
3470 if( !( *m_drawings[ii] == *aOther.m_drawings[ii] ) )
3471 return false;
3472 }
3473
3474 if( m_zones.size() != aOther.m_zones.size() )
3475 return false;
3476
3477 for( size_t ii = 0; ii < m_zones.size(); ++ii )
3478 {
3479 if( !( *m_zones[ii] == *aOther.m_zones[ii] ) )
3480 return false;
3481 }
3482
3483 if( m_fields.size() != aOther.m_fields.size() )
3484 return false;
3485
3486 for( size_t ii = 0; ii < m_fields.size(); ++ii )
3487 {
3488 if( !( *m_fields[ii] == *aOther.m_fields[ii] ) )
3489 return false;
3490 }
3491
3492 return true;
3493}
3494
3495
3496double FOOTPRINT::Similarity( const BOARD_ITEM& aOther ) const
3497{
3498 if( aOther.Type() != PCB_FOOTPRINT_T )
3499 return 0.0;
3500
3501 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
3502
3503 double similarity = 1.0;
3504
3505 for( const PAD* pad : m_pads)
3506 {
3507 const PAD* otherPad = other.FindPadByNumber( pad->GetNumber() );
3508
3509 if( !otherPad )
3510 continue;
3511
3512 similarity *= pad->Similarity( *otherPad );
3513 }
3514
3515 return similarity;
3516}
3517
3518
3519bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* itemA, const BOARD_ITEM* itemB ) const
3520{
3521 if( itemA->Type() != itemB->Type() )
3522 return itemA->Type() < itemB->Type();
3523
3524 if( itemA->GetLayer() != itemB->GetLayer() )
3525 return itemA->GetLayer() < itemB->GetLayer();
3526
3527 if( itemA->Type() == PCB_SHAPE_T )
3528 {
3529 const PCB_SHAPE* dwgA = static_cast<const PCB_SHAPE*>( itemA );
3530 const PCB_SHAPE* dwgB = static_cast<const PCB_SHAPE*>( itemB );
3531
3532 if( dwgA->GetShape() != dwgB->GetShape() )
3533 return dwgA->GetShape() < dwgB->GetShape();
3534
3535 // GetStart() and GetEnd() have no meaning with polygons.
3536 // We cannot use them for sorting polygons
3537 if( dwgA->GetShape() != SHAPE_T::POLY )
3538 {
3539 if( dwgA->GetStart().x != dwgB->GetStart().x )
3540 return dwgA->GetStart().x < dwgB->GetStart().x;
3541 if( dwgA->GetStart().y != dwgB->GetStart().y )
3542 return dwgA->GetStart().y < dwgB->GetStart().y;
3543
3544 if( dwgA->GetEnd().x != dwgB->GetEnd().x )
3545 return dwgA->GetEnd().x < dwgB->GetEnd().x;
3546 if( dwgA->GetEnd().y != dwgB->GetEnd().y )
3547 return dwgA->GetEnd().y < dwgB->GetEnd().y;
3548 }
3549
3550 if( dwgA->GetShape() == SHAPE_T::ARC )
3551 {
3552 if( dwgA->GetCenter().x != dwgB->GetCenter().x )
3553 return dwgA->GetCenter().x < dwgB->GetCenter().x;
3554 if( dwgA->GetCenter().y != dwgB->GetCenter().y )
3555 return dwgA->GetCenter().y < dwgB->GetCenter().y;
3556 }
3557 else if( dwgA->GetShape() == SHAPE_T::BEZIER )
3558 {
3559 if( dwgA->GetBezierC1().x != dwgB->GetBezierC1().x )
3560 return dwgA->GetBezierC1().x < dwgB->GetBezierC1().x;
3561 if( dwgA->GetBezierC1().y != dwgB->GetBezierC1().y )
3562 return dwgA->GetBezierC1().y < dwgB->GetBezierC1().y;
3563
3564 if( dwgA->GetBezierC2().x != dwgB->GetBezierC2().x )
3565 return dwgA->GetBezierC2().x < dwgB->GetBezierC2().x;
3566 if( dwgA->GetBezierC2().y != dwgB->GetBezierC2().y )
3567 return dwgA->GetBezierC2().y < dwgB->GetBezierC2().y;
3568 }
3569 else if( dwgA->GetShape() == SHAPE_T::POLY )
3570 {
3571 if( dwgA->GetPolyShape().TotalVertices() != dwgB->GetPolyShape().TotalVertices() )
3572 return dwgA->GetPolyShape().TotalVertices() < dwgB->GetPolyShape().TotalVertices();
3573
3574 for( int ii = 0; ii < dwgA->GetPolyShape().TotalVertices(); ++ii )
3575 {
3576 if( dwgA->GetPolyShape().CVertex( ii ).x != dwgB->GetPolyShape().CVertex( ii ).x )
3577 return dwgA->GetPolyShape().CVertex( ii ).x
3578 < dwgB->GetPolyShape().CVertex( ii ).x;
3579 if( dwgA->GetPolyShape().CVertex( ii ).y != dwgB->GetPolyShape().CVertex( ii ).y )
3580 return dwgA->GetPolyShape().CVertex( ii ).y
3581 < dwgB->GetPolyShape().CVertex( ii ).y;
3582 }
3583 }
3584
3585 if( dwgA->GetWidth() != dwgB->GetWidth() )
3586 return dwgA->GetWidth() < dwgB->GetWidth();
3587 }
3588
3589 if( itemA->m_Uuid != itemB->m_Uuid )
3590 return itemA->m_Uuid < itemB->m_Uuid;
3591
3592 return itemA < itemB;
3593}
3594
3595
3596bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) const
3597{
3598 if( aFirst->GetNumber() != aSecond->GetNumber() )
3599 return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0;
3600
3601 if( aFirst->GetFPRelativePosition().x != aSecond->GetFPRelativePosition().x )
3602 return aFirst->GetFPRelativePosition().x < aSecond->GetFPRelativePosition().x;
3603 if( aFirst->GetFPRelativePosition().y != aSecond->GetFPRelativePosition().y )
3604 return aFirst->GetFPRelativePosition().y < aSecond->GetFPRelativePosition().y;
3605
3606 std::optional<bool> padCopperMatches;
3607
3608 // Pick the "most complex" padstack to iterate
3609 const PAD* checkPad = aFirst;
3610
3611 if( aSecond->Padstack().Mode() == PADSTACK::MODE::CUSTOM
3612 || ( aSecond->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK &&
3613 aFirst->Padstack().Mode() == PADSTACK::MODE::NORMAL ) )
3614 {
3615 checkPad = aSecond;
3616 }
3617
3618 checkPad->Padstack().ForEachUniqueLayer(
3619 [&]( PCB_LAYER_ID aLayer )
3620 {
3621 if( aFirst->GetSize( aLayer ).x != aSecond->GetSize( aLayer ).x )
3622 padCopperMatches = aFirst->GetSize( aLayer ).x < aSecond->GetSize( aLayer ).x;
3623 else if( aFirst->GetSize( aLayer ).y != aSecond->GetSize( aLayer ).y )
3624 padCopperMatches = aFirst->GetSize( aLayer ).y < aSecond->GetSize( aLayer ).y;
3625 else if( aFirst->GetShape( aLayer ) != aSecond->GetShape( aLayer ) )
3626 padCopperMatches = aFirst->GetShape( aLayer ) < aSecond->GetShape( aLayer );
3627 } );
3628
3629 if( padCopperMatches.has_value() )
3630 return *padCopperMatches;
3631
3632 if( aFirst->GetLayerSet().Seq() != aSecond->GetLayerSet().Seq() )
3633 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
3634
3635 if( aFirst->m_Uuid != aSecond->m_Uuid )
3636 return aFirst->m_Uuid < aSecond->m_Uuid;
3637
3638 return aFirst < aSecond;
3639}
3640
3641
3642#if 0
3643bool FOOTPRINT::cmp_padstack::operator()( const PAD* aFirst, const PAD* aSecond ) const
3644{
3645 if( aFirst->GetSize().x != aSecond->GetSize().x )
3646 return aFirst->GetSize().x < aSecond->GetSize().x;
3647 if( aFirst->GetSize().y != aSecond->GetSize().y )
3648 return aFirst->GetSize().y < aSecond->GetSize().y;
3649
3650 if( aFirst->GetShape() != aSecond->GetShape() )
3651 return aFirst->GetShape() < aSecond->GetShape();
3652
3653 if( aFirst->GetLayerSet().Seq() != aSecond->GetLayerSet().Seq() )
3654 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
3655
3656 if( aFirst->GetDrillSizeX() != aSecond->GetDrillSizeX() )
3657 return aFirst->GetDrillSizeX() < aSecond->GetDrillSizeX();
3658
3659 if( aFirst->GetDrillSizeY() != aSecond->GetDrillSizeY() )
3660 return aFirst->GetDrillSizeY() < aSecond->GetDrillSizeY();
3661
3662 if( aFirst->GetDrillShape() != aSecond->GetDrillShape() )
3663 return aFirst->GetDrillShape() < aSecond->GetDrillShape();
3664
3665 if( aFirst->GetAttribute() != aSecond->GetAttribute() )
3666 return aFirst->GetAttribute() < aSecond->GetAttribute();
3667
3668 if( aFirst->GetOrientation() != aSecond->GetOrientation() )
3669 return aFirst->GetOrientation() < aSecond->GetOrientation();
3670
3671 if( aFirst->GetSolderMaskExpansion() != aSecond->GetSolderMaskExpansion() )
3672 return aFirst->GetSolderMaskExpansion() < aSecond->GetSolderMaskExpansion();
3673
3674 if( aFirst->GetSolderPasteMargin() != aSecond->GetSolderPasteMargin() )
3675 return aFirst->GetSolderPasteMargin() < aSecond->GetSolderPasteMargin();
3676
3677 if( aFirst->GetLocalSolderMaskMargin() != aSecond->GetLocalSolderMaskMargin() )
3678 return aFirst->GetLocalSolderMaskMargin() < aSecond->GetLocalSolderMaskMargin();
3679
3680 const std::shared_ptr<SHAPE_POLY_SET>& firstShape = aFirst->GetEffectivePolygon( ERROR_INSIDE );
3681 const std::shared_ptr<SHAPE_POLY_SET>& secondShape = aSecond->GetEffectivePolygon( ERROR_INSIDE );
3682
3683 if( firstShape->VertexCount() != secondShape->VertexCount() )
3684 return firstShape->VertexCount() < secondShape->VertexCount();
3685
3686 for( int ii = 0; ii < firstShape->VertexCount(); ++ii )
3687 {
3688 if( firstShape->CVertex( ii ).x != secondShape->CVertex( ii ).x )
3689 return firstShape->CVertex( ii ).x < secondShape->CVertex( ii ).x;
3690 if( firstShape->CVertex( ii ).y != secondShape->CVertex( ii ).y )
3691 return firstShape->CVertex( ii ).y < secondShape->CVertex( ii ).y;
3692 }
3693
3694 return false;
3695}
3696#endif
3697
3698
3699bool FOOTPRINT::cmp_zones::operator()( const ZONE* aFirst, const ZONE* aSecond ) const
3700{
3701 if( aFirst->GetAssignedPriority() != aSecond->GetAssignedPriority() )
3702 return aFirst->GetAssignedPriority() < aSecond->GetAssignedPriority();
3703
3704 if( aFirst->GetLayerSet().Seq() != aSecond->GetLayerSet().Seq() )
3705 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
3706
3707 if( aFirst->Outline()->TotalVertices() != aSecond->Outline()->TotalVertices() )
3708 return aFirst->Outline()->TotalVertices() < aSecond->Outline()->TotalVertices();
3709
3710 for( int ii = 0; ii < aFirst->Outline()->TotalVertices(); ++ii )
3711 {
3712 if( aFirst->Outline()->CVertex( ii ).x != aSecond->Outline()->CVertex( ii ).x )
3713 return aFirst->Outline()->CVertex( ii ).x < aSecond->Outline()->CVertex( ii ).x;
3714 if( aFirst->Outline()->CVertex( ii ).y != aSecond->Outline()->CVertex( ii ).y )
3715 return aFirst->Outline()->CVertex( ii ).y < aSecond->Outline()->CVertex( ii ).y;
3716 }
3717
3718 if( aFirst->m_Uuid != aSecond->m_Uuid )
3719 return aFirst->m_Uuid < aSecond->m_Uuid;
3720
3721 return aFirst < aSecond;
3722}
3723
3724
3726 int aClearance, int aMaxError, ERROR_LOC aErrorLoc,
3727 bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads,
3728 bool aSkipNonPlatedPads ) const
3729{
3730 auto processPad =
3731 [&]( const PAD* pad, PCB_LAYER_ID padLayer )
3732 {
3733 VECTOR2I clearance( aClearance, aClearance );
3734
3735 switch( aLayer )
3736 {
3737 case F_Cu:
3738 if( aSkipPlatedPads && pad->FlashLayer( F_Mask ) )
3739 return;
3740
3741 if( aSkipNonPlatedPads && !pad->FlashLayer( F_Mask ) )
3742 return;
3743
3744 break;
3745
3746 case B_Cu:
3747 if( aSkipPlatedPads && pad->FlashLayer( B_Mask ) )
3748 return;
3749
3750 if( aSkipNonPlatedPads && !pad->FlashLayer( B_Mask ) )
3751 return;
3752
3753 break;
3754
3755 case F_Mask:
3756 case B_Mask:
3757 clearance.x += pad->GetSolderMaskExpansion( padLayer );
3758 clearance.y += pad->GetSolderMaskExpansion( padLayer );
3759 break;
3760
3761 case F_Paste:
3762 case B_Paste:
3763 clearance += pad->GetSolderPasteMargin( padLayer );
3764 break;
3765
3766 default:
3767 break;
3768 }
3769
3770 // Our standard TransformShapeToPolygon() routines can't handle differing x:y clearance
3771 // values (which get generated when a relative paste margin is used with an oblong pad).
3772 // So we apply this huge hack and fake a larger pad to run the transform on.
3773 // Of course being a hack it falls down when dealing with custom shape pads (where the
3774 // size is only the size of the anchor), so for those we punt and just use clearance.x.
3775
3776 if( ( clearance.x < 0 || clearance.x != clearance.y )
3777 && pad->GetShape( padLayer ) != PAD_SHAPE::CUSTOM )
3778 {
3779 VECTOR2I dummySize = pad->GetSize( padLayer ) + clearance + clearance;
3780
3781 if( dummySize.x <= 0 || dummySize.y <= 0 )
3782 return;
3783
3784 PAD dummy( *pad );
3785 dummy.SetSize( padLayer, dummySize );
3786 dummy.TransformShapeToPolygon( aBuffer, padLayer, 0, aMaxError, aErrorLoc );
3787 }
3788 else
3789 {
3790 pad->TransformShapeToPolygon( aBuffer, padLayer, clearance.x, aMaxError,
3791 aErrorLoc );
3792 }
3793 };
3794
3795 for( const PAD* pad : m_pads )
3796 {
3797 if( !pad->FlashLayer( aLayer ) )
3798 continue;
3799
3800 if( aLayer == UNDEFINED_LAYER )
3801 {
3802 pad->Padstack().ForEachUniqueLayer(
3803 [&]( PCB_LAYER_ID l )
3804 {
3805 processPad( pad, l );
3806 } );
3807 }
3808 else
3809 {
3810 processPad( pad, aLayer );
3811 }
3812 }
3813}
3814
3815
3817 int aClearance, int aError, ERROR_LOC aErrorLoc,
3818 bool aIncludeText, bool aIncludeShapes,
3819 bool aIncludePrivateItems ) const
3820{
3821 std::vector<const PCB_TEXT*> texts; // List of PCB_TEXTs to convert
3822
3823 for( BOARD_ITEM* item : GraphicalItems() )
3824 {
3825 if( GetPrivateLayers().test( item->GetLayer() ) && !aIncludePrivateItems )
3826 continue;
3827
3828 if( item->Type() == PCB_TEXT_T && aIncludeText )
3829 {
3830 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
3831
3832 if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
3833 texts.push_back( text );
3834 }
3835
3836 if( item->Type() == PCB_TEXTBOX_T && aIncludeText )
3837 {
3838 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
3839
3840 if( aLayer != UNDEFINED_LAYER && textbox->GetLayer() == aLayer && textbox->IsVisible() )
3841 {
3842 // border
3843 if( textbox->IsBorderEnabled() )
3844 textbox->PCB_SHAPE::TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
3845 // text
3846 textbox->TransformTextToPolySet( aBuffer, 0, aError, aErrorLoc );
3847 }
3848 }
3849
3850 if( item->Type() == PCB_SHAPE_T && aIncludeShapes )
3851 {
3852 const PCB_SHAPE* outline = static_cast<PCB_SHAPE*>( item );
3853
3854 if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
3855 outline->TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
3856 }
3857 }
3858
3859 if( aIncludeText )
3860 {
3861 for( const PCB_FIELD* field : m_fields )
3862 {
3863 if( field->GetLayer() == aLayer && field->IsVisible() )
3864 texts.push_back( field );
3865 }
3866 }
3867
3868 for( const PCB_TEXT* text : texts )
3869 text->TransformTextToPolySet( aBuffer, aClearance, aError, aErrorLoc );
3870}
3871
3872
3874{
3875 using OUTLINE_FONT = KIFONT::OUTLINE_FONT;
3876 using EMBEDDING_PERMISSION = OUTLINE_FONT::EMBEDDING_PERMISSION;
3877
3878 std::set<OUTLINE_FONT*> fonts;
3879
3880 for( BOARD_ITEM* item : GraphicalItems() )
3881 {
3882 if( auto* text = dynamic_cast<EDA_TEXT*>( item ) )
3883 {
3884 if( auto* font = text->GetFont(); font && !font->IsStroke() )
3885 {
3886 auto* outline = static_cast<OUTLINE_FONT*>( font );
3887 auto permission = outline->GetEmbeddingPermission();
3888
3889 if( permission == EMBEDDING_PERMISSION::EDITABLE
3890 || permission == EMBEDDING_PERMISSION::INSTALLABLE )
3891 {
3892 fonts.insert( outline );
3893 }
3894 }
3895 }
3896 }
3897
3898 for( auto* font : fonts )
3899 {
3900 auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
3902 }
3903}
3904
3905
3907{
3908 if( m_componentClass )
3909 {
3910 return m_componentClass->GetFullName();
3911 }
3912
3913 return wxEmptyString;
3914}
3915
3916
3917static struct FOOTPRINT_DESC
3918{
3920 {
3922
3923 if( zcMap.Choices().GetCount() == 0 )
3924 {
3925 zcMap.Undefined( ZONE_CONNECTION::INHERITED );
3926 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
3927 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
3928 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
3929 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
3930 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
3931 }
3932
3934
3935 if( layerEnum.Choices().GetCount() == 0 )
3936 {
3937 layerEnum.Undefined( UNDEFINED_LAYER );
3938
3939 for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
3940 layerEnum.Map( layer, LSET::Name( layer ) );
3941 }
3942
3943 wxPGChoices fpLayers; // footprints might be placed only on F.Cu & B.Cu
3944 fpLayers.Add( LSET::Name( F_Cu ), F_Cu );
3945 fpLayers.Add( LSET::Name( B_Cu ), B_Cu );
3946
3953
3954 auto layer = new PROPERTY_ENUM<FOOTPRINT, PCB_LAYER_ID>( _HKI( "Layer" ),
3956 layer->SetChoices( fpLayers );
3957 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
3958
3959 propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Orientation" ),
3961 PROPERTY_DISPLAY::PT_DEGREE ) );
3962
3963 const wxString groupFields = _HKI( "Fields" );
3964
3965 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Reference" ),
3967 groupFields );
3968 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Value" ),
3970 groupFields );
3971
3972 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Link" ),
3974 groupFields );
3975 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Description" ),
3977 groupFields );
3978 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Keywords" ),
3980 groupFields );
3981
3982 // Used only in DRC engine
3984 _HKI( "Component Class" ), NO_SETTER( FOOTPRINT, wxString ),
3988
3989 const wxString groupAttributes = _HKI( "Attributes" );
3990
3991 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Not in Schematic" ),
3992 &FOOTPRINT::SetBoardOnly, &FOOTPRINT::IsBoardOnly ), groupAttributes );
3993 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Position Files" ),
3995 groupAttributes );
3996 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Bill of Materials" ),
3998 groupAttributes );
3999 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Do not Populate" ),
4001 groupAttributes );
4002
4003 const wxString groupOverrides = _HKI( "Overrides" );
4004
4006 _HKI( "Exempt From Courtyard Requirement" ),
4008 groupOverrides );
4009 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>(
4010 _HKI( "Clearance Override" ),
4012 PROPERTY_DISPLAY::PT_SIZE ),
4013 groupOverrides );
4014 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>(
4015 _HKI( "Solderpaste Margin Override" ),
4017 PROPERTY_DISPLAY::PT_SIZE ),
4018 groupOverrides );
4019 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<double>>(
4020 _HKI( "Solderpaste Margin Ratio Override" ),
4023 PROPERTY_DISPLAY::PT_RATIO ),
4024 groupOverrides );
4026 _HKI( "Zone Connection Style" ),
4028 groupOverrides );
4029 }
const char * name
Definition: DXF_plotter.cpp:57
std::unique_ptr< EDA_ITEM > CreateItemForType(KICAD_T aType, EDA_ITEM *aContainer)
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
constexpr int ARC_LOW_DEF
Definition: base_units.h:119
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:33
#define DEFAULT_COURTYARD_WIDTH
BASE_SET & set(size_t pos)
Definition: base_set.h:115
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:237
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:90
PCB_LAYER_ID m_layer
Definition: board_item.h:436
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:255
void SetX(int aX)
Definition: board_item.h:116
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:243
void SetY(int aY)
Definition: board_item.h:122
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:288
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:47
VECTOR2I GetFPRelativePosition() const
Definition: board_item.cpp:327
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:215
virtual bool IsOnCopperLayer() const
Definition: board_item.h:150
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
Definition: board_item.cpp:139
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
bool IsFootprintHolder() const
Find out if the board is being used to hold a single footprint for editing/viewing.
Definition: board.h:320
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:844
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition: board.cpp:784
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
int GetTimeStamp() const
Definition: board.h:313
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 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 const Vec GetCenter() const
Definition: box2.h:230
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition: box2.h:138
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
constexpr coord_type GetBottom() const
Definition: box2.h:222
virtual int Accuracy() const =0
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
const wxString & GetFullName() const
Fetches the full name of this component class.
wxString GetName() const
Fetches the display name of this component class.
EDA_ANGLE Normalize180()
Definition: eda_angle.h:260
bool IsType(FRAME_T aType) const
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 void ClearEditFlags()
Definition: eda_item.h:141
EDA_ITEM & operator=(const EDA_ITEM &aItem)
Assign the members of aItem to another object.
Definition: eda_item.cpp:261
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
const KIID m_Uuid
Definition: eda_item.h:489
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:129
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:104
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:131
virtual wxString GetClass() const =0
Return the class name.
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:130
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:213
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:286
bool IsFilled() const
Definition: eda_shape.h:98
SHAPE_T GetShape() const
Definition: eda_shape.h:132
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:210
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:408
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:377
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:269
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:291
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:400
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Loads a file from disk and adds it to the collection.
const std::map< wxString, EMBEDDED_FILE * > & EmbeddedFileMap() const
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition: property.h:669
static ENUM_MAP< T > & Instance()
Definition: property.h:663
ENUM_MAP & Undefined(T aValue)
Definition: property.h:676
wxPGChoices & Choices()
Definition: property.h:712
bool FixUuids()
Old footprints do not always have a valid UUID (some can be set to null uuid) However null UUIDs,...
Definition: footprint.cpp:692
void EmbedFonts() override
Definition: footprint.cpp:3873
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:2388
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:249
LIB_ID m_fpid
Definition: footprint.h:1062
wxString GetLibDescription() const
Definition: footprint.h:257
ZONE_CONNECTION GetLocalZoneConnection() const
Definition: footprint.h:288
const COMPONENT_CLASS * m_componentClass
Definition: footprint.h:1120
KIID_PATH m_path
Definition: footprint.h:1101
bool IsBoardOnly() const
Definition: footprint.h:750
BOX2I m_cachedTextExcludedBBox
Definition: footprint.h:1079
bool IsDNP() const
Definition: footprint.h:786
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:421
HASH_128 m_courtyard_cache_back_hash
Definition: footprint.h:1117
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
Definition: footprint.cpp:3139
bool ResolveTextVar(wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the component.
Definition: footprint.cpp:948
EDA_ANGLE GetOrientation() const
Definition: footprint.h:227
bool HasFieldByName(const wxString &aFieldName) const
Definition: footprint.cpp:578
ZONES & Zones()
Definition: footprint.h:212
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: footprint.cpp:385
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1088
ZONE_CONNECTION m_zoneConnection
Definition: footprint.h:1092
BOX2I m_cachedBoundingBox
Definition: footprint.h:1077
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
Definition: footprint.cpp:2699
bool IsExcludedFromBOM() const
Definition: footprint.h:768
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:2458
std::optional< double > m_solderPasteMarginRatio
Definition: footprint.h:1096
PCB_FIELD * GetFieldByName(const wxString &aFieldName)
Return a field in this symbol.
Definition: footprint.cpp:589
void SetDNP(bool aDNP=true)
Definition: footprint.h:787
static bool IsLibNameValid(const wxString &aName)
Test for validity of a name of a footprint to be used in a footprint library ( no spaces,...
Definition: footprint.cpp:2256
unsigned GetPadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the number of pads.
Definition: footprint.cpp:1904
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition: footprint.h:285
const BOX2I GetLayerBoundingBox(LSET aLayers) const
Return the bounding box of the footprint on a given set of layers.
Definition: footprint.cpp:1441
std::optional< int > m_clearance
Definition: footprint.h:1093
PCB_FIELD * AddField(const PCB_FIELD &aField)
Add a field to the symbol.
Definition: footprint.cpp:637
void CheckNetTies(const std::function< void(const BOARD_ITEM *aItem, const BOARD_ITEM *bItem, const BOARD_ITEM *cItem, const VECTOR2I &)> &aErrorHandler)
Check for un-allowed shorting of pads in net-tie footprints.
Definition: footprint.cpp:3273
void CheckPads(UNITS_PROVIDER *aUnitsProvider, const std::function< void(const PAD *, int, const wxString &)> &aErrorHandler)
Run non-board-specific DRC checks on footprint's pads.
Definition: footprint.cpp:3186
void SetExcludedFromBOM(bool aExclude=true)
Definition: footprint.h:769
int m_fpStatus
Definition: footprint.h:1064
PCB_FIELDS GetFields()
Return a vector of fields from the symbol.
Definition: footprint.h:716
double ViewGetLOD(int aLayer, KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
Definition: footprint.cpp:2200
SHAPE_POLY_SET m_cachedHull
Definition: footprint.h:1081
std::set< wxString > GetUniquePadNumbers(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the names of the unique, non-blank pads.
Definition: footprint.cpp:1923
void SetAllowMissingCourtyard(bool aAllow=true)
Definition: footprint.h:778
void SetKeywords(const wxString &aKeywords)
Definition: footprint.h:261
int m_attributes
Definition: footprint.h:1063
PCB_LAYER_ID GetSide() const
Use instead of IsFlipped() when you also need to account for unsided footprints (those purely on user...
Definition: footprint.cpp:1658
std::vector< FP_3DMODEL > m_3D_Drawings
Definition: footprint.h:1110
double CoverageRatio(const GENERAL_COLLECTOR &aCollector) const
Calculate the ratio of total area of the footprint pads and graphical items to the area of the footpr...
Definition: footprint.cpp:2792
BOARD_ITEM * DuplicateItem(const BOARD_ITEM *aItem, bool aAddToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
Definition: footprint.cpp:2497
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: footprint.cpp:2856
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction) const override
Invoke a function on all descendants.
Definition: footprint.cpp:2081
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
Definition: footprint.cpp:2069
void RunOnDescendants(const std::function< void(BOARD_ITEM *)> &aFunction, int aDepth=0) const override
Invoke a function on all descendants.
Definition: footprint.cpp:2107
std::optional< int > GetLocalSolderPasteMargin() const
Definition: footprint.h:281
void TransformPadsToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, bool aSkipNPTHPadsWihNoCopper=false, bool aSkipPlatedPads=false, bool aSkipNonPlatedPads=false) const
Generate pads shapes on layer aLayer as polygons and adds these polygons to aBuffer.
Definition: footprint.cpp:3725
wxArrayString * m_initial_comments
Definition: footprint.h:1111
EDA_ITEM * Clone() const override
Invoke a function on all children.
Definition: footprint.cpp:2075
BOX2I GetFpPadsLocalBbox() const
Return the bounding box containing pads when the footprint is on the front side, orientation 0,...
Definition: footprint.cpp:1255
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:658
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: footprint.cpp:2294
std::optional< int > m_solderPasteMargin
Definition: footprint.h:1095
std::mutex m_courtyard_cache_mutex
Definition: footprint.h:1118
void SetExcludedFromPosFiles(bool aExclude=true)
Definition: footprint.h:760
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:239
void SetPrivateLayers(LSET aLayers)
Adds an item to the container.
Definition: footprint.h:142
void ApplyDefaultSettings(const BOARD &board, bool aStyleFields, bool aStyleText, bool aStyleShapes)
Apply default board settings to the footprint field text properties.
Definition: footprint.cpp:659
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3084
std::optional< int > GetLocalClearance() const
Definition: footprint.h:275
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
Definition: footprint.cpp:3496
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:2414
FOOTPRINT & operator=(const FOOTPRINT &aOther)
Definition: footprint.cpp:824
int m_arflag
Definition: footprint.h:1106
double GetOrientationDegrees() const
Definition: footprint.h:243
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:2484
INSPECT_RESULT Visit(INSPECTOR inspector, void *testData, const std::vector< KICAD_T > &aScanTypes) override
May be re-implemented for each derived class in order to handle all the types given by its member dat...
Definition: footprint.cpp:1971
std::deque< PAD * > & Pads()
Definition: footprint.h:206
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: footprint.cpp:272
int GetAttributes() const
Definition: footprint.h:290
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition: footprint.h:287
FOOTPRINT(BOARD *parent)
Definition: footprint.cpp:69
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:236
LSET GetPrivateLayers() const
Definition: footprint.h:141
wxString GetFPIDAsString() const
Definition: footprint.h:254
wxString GetValueAsString() const
Definition: footprint.h:652
bool AllowMissingCourtyard() const
Definition: footprint.h:777
wxString GetComponentClassAsString() const
Definition: footprint.cpp:3906
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
Definition: footprint.cpp:1486
void TransformFPShapesToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIncludeText=true, bool aIncludeShapes=true, bool aIncludePrivateItems=false) const
Generate shapes of graphic items (outlines) on layer aLayer as polygons and adds these polygons to aB...
Definition: footprint.cpp:3816
wxString GetTypeName() const
Get the type of footprint.
Definition: footprint.cpp:1243
DRAWINGS m_drawings
Definition: footprint.h:1055
PCB_FIELD * GetFieldById(int aFieldId)
Return a field in this symbol.
Definition: footprint.cpp:567
SHAPE_POLY_SET m_courtyard_cache_back
Definition: footprint.h:1115
int m_textExcludedBBoxCacheTimeStamp
Definition: footprint.h:1080
const std::vector< wxString > & GetNetTiePadGroups() const
Definition: footprint.h:339
const LIB_ID & GetFPID() const
Definition: footprint.h:248
void SetReference(const wxString &aReference)
Definition: footprint.h:628
bool IsLocked() const override
Definition: footprint.h:411
bool IsExcludedFromPosFiles() const
Definition: footprint.h:759
void SetLayerAndFlip(PCB_LAYER_ID aLayer)
Used as Layer property setter – performs a flip if necessary to set the footprint layer.
Definition: footprint.cpp:2321
ZONES m_zones
Definition: footprint.h:1057
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with PCB_TEXTs.
Definition: footprint.cpp:616
int m_hullCacheTimeStamp
Definition: footprint.h:1082
unsigned GetUniquePadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the number of unique non-blank pads.
Definition: footprint.cpp:1954
void AddNetTiePadGroup(const wxString &aGroup)
Definition: footprint.h:346
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: footprint.cpp:2241
LSET m_privateLayers
Definition: footprint.h:1108
int GetLikelyAttribute() const
Returns the most likely attribute based on pads Either FP_THROUGH_HOLE/FP_SMD/OTHER(0)
Definition: footprint.cpp:1190
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: footprint.cpp:2284
wxString m_libDescription
Definition: footprint.h:1099
bool HitTestOnLayer(const VECTOR2I &aPosition, PCB_LAYER_ID aLayer, int aAccuracy=0) const
Test if the point hits one or more of the footprint elements on a given layer.
Definition: footprint.cpp:1720
VECTOR2I m_pos
Definition: footprint.h:1061
std::vector< wxString > m_netTiePadGroups
Definition: footprint.h:1086
PAD * GetPad(const VECTOR2I &aPosition, LSET aLayerMask=LSET::AllLayersMask())
Get a pad at aPosition on aLayerMask in the footprint.
Definition: footprint.cpp:1872
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Definition: footprint.cpp:2144
void Add3DModel(FP_3DMODEL *a3DModel)
Add a3DModel definition to the end of the 3D model list.
Definition: footprint.cpp:1960
void SetValue(const wxString &aValue)
Definition: footprint.h:649
GROUPS m_groups
Definition: footprint.h:1058
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: footprint.cpp:1573
PCB_FIELD & Reference()
Definition: footprint.h:659
wxString GetReferenceAsString() const
Definition: footprint.h:631
void GetContextualTextVars(wxArrayString *aVars) const
Return the list of system text vars for this footprint.
Definition: footprint.cpp:934
std::optional< int > m_solderMaskMargin
Definition: footprint.h:1094
SHAPE_POLY_SET m_courtyard_cache_front
Definition: footprint.h:1114
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1025
std::vector< const PAD * > GetPads(const wxString &aPadNumber, const PAD *aIgnore=nullptr) const
Definition: footprint.cpp:1888
void AutoPositionFields()
Position Reference and Value fields at the top and bottom of footprint's bounding box.
Definition: footprint.cpp:2644
wxString m_keywords
Definition: footprint.h:1100
void ClearAllNets()
Clear (i.e.
Definition: footprint.cpp:1016
std::deque< PAD * > m_pads
Definition: footprint.h:1056
bool HasThroughHolePads() const
Definition: footprint.cpp:3431
void BuildCourtyardCaches(OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Build complex polygons of the courtyard areas from graphic items on the courtyard layers.
Definition: footprint.cpp:2915
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: footprint.cpp:1689
wxString GetFieldText(const wxString &aFieldName) const
Search for a field named aFieldName and returns text associated with this field.
Definition: footprint.cpp:604
EDA_ANGLE m_orient
Definition: footprint.h:1060
bool HitTestAccurate(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if a point is inside the bounding polygon of the footprint.
Definition: footprint.cpp:1795
wxString GetClass() const override
Return the class name.
Definition: footprint.h:892
void IncrementReference(int aDelta)
Bump the current reference by aDelta.
Definition: footprint.cpp:2672
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition: footprint.h:284
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition: footprint.cpp:2330
KIID m_link
Definition: footprint.h:1107
void RemoveField(const wxString &aFieldName)
Remove a user field from the footprint.
Definition: footprint.cpp:646
GROUPS & Groups()
Definition: footprint.h:215
void BuildNetTieCache()
Cache the pads that are allowed to connect to each other in the footprint.
Definition: footprint.cpp:3017
bool IsConflicting() const
Definition: footprint.cpp:928
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:220
const SHAPE_POLY_SET & GetCachedCourtyard(PCB_LAYER_ID aLayer) const
Return the cached courtyard area.
Definition: footprint.cpp:2906
PCB_FIELDS & Fields()
Definition: footprint.h:203
static const wxChar * StringLibNameInvalidChars(bool aUserReadable)
Test for validity of the name in a library of the footprint ( no spaces, dir separators ....
Definition: footprint.cpp:2267
void SetLibDescription(const wxString &aDesc)
Definition: footprint.h:258
bool TextOnly() const
Definition: footprint.cpp:1280
void CheckShortingPads(const std::function< void(const PAD *, const PAD *, int aErrorCode, const VECTOR2I &)> &aErrorHandler)
Check for overlapping, different-numbered, non-net-tie pads.
Definition: footprint.cpp:3204
double GetArea(int aPadding=0) const
Definition: footprint.cpp:1180
const wxString & GetReference() const
Definition: footprint.h:622
void CheckNetTiePadGroups(const std::function< void(const wxString &)> &aErrorHandler)
Sanity check net-tie pad groups.
Definition: footprint.cpp:3386
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition: footprint.cpp:2058
void SetBoardOnly(bool aIsBoardOnly=true)
Definition: footprint.h:751
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: footprint.h:279
PCB_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: footprint.cpp:555
timestamp_t m_lastEditTime
Definition: footprint.h:1105
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2892
std::map< const BOARD_ITEM *, std::set< int > > m_netTieCache
Definition: footprint.h:1089
int m_fileFormatVersionAtLoad
Definition: footprint.h:1065
void SetLocalClearance(std::optional< int > aClearance)
Definition: footprint.h:276
std::optional< int > GetLocalSolderMaskMargin() const
Definition: footprint.h:278
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition: footprint.h:282
wxString GetKeywords() const
Definition: footprint.h:260
bool operator==(const BOARD_ITEM &aOther) const override
Definition: footprint.cpp:3443
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: footprint.h:1001
void CheckFootprintAttributes(const std::function< void(const wxString &)> &aErrorHandler)
Test if footprint attributes for type (SMD/Through hole/Other) match the expected type based on the p...
Definition: footprint.cpp:3161
virtual void swapData(BOARD_ITEM *aImage) override
Definition: footprint.cpp:3409
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:2624
int m_boundingBoxCacheTimeStamp
Definition: footprint.h:1078
VECTOR2I GetPosition() const override
Definition: footprint.h:224
DRAWINGS & GraphicalItems()
Definition: footprint.h:209
PCB_FIELDS m_fields
Definition: footprint.h:1054
HASH_128 m_courtyard_cache_front_hash
Definition: footprint.h:1116
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: footprint.cpp:1788
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
Definition: footprint.cpp:1852
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1295
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:101
double m_Opacity
Definition: footprint.h:102
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:100
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:99
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:103
bool m_Show
Include model in rendering.
Definition: footprint.h:104
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:202
const COLLECTORS_GUIDE * GetGuide() const
Definition: collectors.h:288
Class OUTLINE_FONT implements outline font drawing.
Definition: outline_font.h:53
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:419
Definition: kiid.h:49
std::string AsStdString() const
Definition: kiid.cpp:244
wxString GetUniStringLibId() const
Definition: lib_id.h:148
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition: lib_id.h:112
const wxString GetUniStringLibNickname() const
Definition: lib_id.h:88
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllLayersMask()
Definition: lset.cpp:711
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:686
static LSET SideSpecificMask()
Definition: lset.cpp:795
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:420
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:193
void ShapeToPolygon(SHAPE_LINE_CHAIN &aPolygon, int aScale=-1) const
Return the shape polygon in internal units in a SHAPE_LINE_CHAIN the coordinates are relatives to the...
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:385
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
Definition: padstack.cpp:870
@ NORMAL
Shape is the same on all layers.
@ CUSTOM
Shapes can be defined on arbitrary layers.
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
MODE Mode() const
Definition: padstack.h:287
Definition: pad.h:54
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:439
int GetDrillSizeY() const
Definition: pad.h:311
PAD_ATTRIB GetAttribute() const
Definition: pad.h:442
const wxString & GetNumber() const
Definition: pad.h:134
VECTOR2I GetPosition() const override
Definition: pad.h:210
int GetDrillSizeX() const
Definition: pad.h:309
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1082
const PADSTACK & Padstack() const
Definition: pad.h:323
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:410
PAD_DRILL_SHAPE GetDrillShape() const
Definition: pad.h:424
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition: pad.cpp:476
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pad.h:460
VECTOR2I GetSolderPasteMargin(PCB_LAYER_ID aLayer) const
Usually < 0 (mask shape smaller than pad)because the margin can be dependent on the pad size,...
Definition: pad.cpp:1128
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:266
Abstract dimension API.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: pcb_field.cpp:49
int GetId() const
Definition: pcb_field.h:107
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: pcb_field.cpp:64
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:69
virtual bool RemoveItem(BOARD_ITEM *aItem)
Remove item from group.
Definition: pcb_group.cpp:96
virtual bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:81
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
int GetWidth() const override
Definition: pcb_shape.cpp:301
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
Definition: pcb_shape.cpp:774
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:89
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:69
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Function TransformTextToPolySet Convert the text to a polygonSet describing the actual character stro...
bool IsVisible() const override
Definition: pcb_textbox.h:101
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: pcb_text.cpp:133
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_text.cpp:334
PROPERTY_BASE & SetIsHiddenFromPropertiesManager(bool aHide=true)
Definition: property.h:300
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.
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:87
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
double Area(bool aAbsolute=true) const
Return the area of this chain.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
void SetWidth(int aWidth)
Set the width of all segments in the chain.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
HASH_128 GetHash() const
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
virtual void CacheTriangulation(bool aPartition=true, bool aSimplify=false)
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
double Area()
Return the area of this poly set.
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union For aFastMode meaning, see function booleanOp.
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,...
int TotalVertices() const
Return total number of vertices stored in the set.
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
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 Simplify(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:42
An abstract shape on 2D plane.
Definition: shape.h:126
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition: shape.h:181
int GetWidth() const
Definition: stroke_params.h:89
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
SHAPE_POLY_SET * Outline()
Definition: zone.h:340
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:133
unsigned GetAssignedPriority() const
Definition: zone.h:123
This file is part of the common library.
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aShapeList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint, OUTLINE_ERROR_HANDLER *aErrorHandler, bool aAllowUseArcsInPolygons)
Build a polygon set with holes from a PCB_SHAPE list.
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const VECTOR2I &pt)> OUTLINE_ERROR_HANDLER
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
Definition: convex_hull.cpp:87
#define _HKI(x)
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition: drc_item.h:52
@ DRCE_SHORTING_ITEMS
Definition: drc_item.h:40
@ DRCE_DRILLED_HOLES_COLOCATED
Definition: drc_item.h:53
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
INSPECT_RESULT
Definition: eda_item.h:43
const INSPECTOR_FUNC & INSPECTOR
Definition: eda_item.h:82
#define COURTYARD_CONFLICT
temporary set when moving footprints having courtyard overlapping
#define MALFORMED_F_COURTYARD
#define MALFORMED_B_COURTYARD
#define STRUCT_DELETED
flag indication structures to be erased
#define MALFORMED_COURTYARDS
#define MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY
static struct FOOTPRINT_DESC _FOOTPRINT_DESC
static double polygonArea(SHAPE_POLY_SET &aPolySet)
Definition: footprint.cpp:2683
INCLUDE_NPTH_T
Definition: footprint.h:63
@ DO_NOT_INCLUDE_NPTH
Definition: footprint.h:64
@ FP_SMD
Definition: footprint.h:76
@ FP_DNP
Definition: footprint.h:83
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:77
@ FP_BOARD_ONLY
Definition: footprint.h:79
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:78
@ FP_THROUGH_HOLE
Definition: footprint.h:75
#define FP_is_PLACED
In autoplace: footprint automatically placed.
Definition: footprint.h:406
#define FP_PADS_are_LOCKED
Definition: footprint.h:408
@ FRAME_FOOTPRINT_VIEWER
Definition: frame_type.h:45
@ FRAME_FOOTPRINT_CHOOSER
Definition: frame_type.h:44
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition: api_utils.cpp:26
Some functions to handle hotkeys in KiCad.
KIID niluuid(0)
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition: layer_id.cpp:208
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition: layer_ids.h:147
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:655
@ LAYER_LOCKED_ITEM_SHADOW
shadow layer for locked items
Definition: layer_ids.h:240
@ LAYER_CONFLICTS_SHADOW
shadow layer for items flagged conficting
Definition: layer_ids.h:242
@ LAYER_FOOTPRINTS_FR
show footprints on front
Definition: layer_ids.h:209
@ LAYER_FP_REFERENCES
show footprints references (when texts are visible)
Definition: layer_ids.h:212
@ LAYER_FP_TEXT
Definition: layer_ids.h:199
@ LAYER_FOOTPRINTS_BK
show footprints on back
Definition: layer_ids.h:210
@ LAYER_ANCHOR
anchor of items having an anchor point (texts, footprints)
Definition: layer_ids.h:202
@ LAYER_FP_VALUES
show footprints values (when texts are visible)
Definition: layer_ids.h:211
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ Dwgs_User
Definition: layer_ids.h:107
@ F_Paste
Definition: layer_ids.h:104
@ Cmts_User
Definition: layer_ids.h:108
@ 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_Fab
Definition: layer_ids.h:119
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Eco2_User
Definition: layer_ids.h:110
@ In1_Cu
Definition: layer_ids.h:66
@ B_SilkS
Definition: layer_ids.h:101
@ 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
wxString GetRefDesPrefix(const wxString &aRefDes)
Get the (non-numeric) prefix from a refdes - e.g.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition: api_utils.cpp:32
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
Definition: api_utils.cpp:98
KICOMMON_API LIB_ID LibIdFromProto(const types::LibraryIdentifier &aId)
Definition: api_utils.cpp:62
KICOMMON_API types::LibraryIdentifier LibIdToProto(const LIB_ID &aId)
Definition: api_utils.cpp:68
KICOMMON_API void PackVector3D(types::Vector3D &aOutput, const VECTOR3D &aInput)
Definition: api_utils.cpp:90
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
Class to handle a set of BOARD_ITEMs.
#define TYPE_HASH(x)
Definition: property.h:71
#define NO_SETTER(owner, type)
Definition: property.h:774
#define REGISTER_TYPE(x)
Definition: property_mgr.h:371
Collection of utility functions for component reference designators (refdes)
std::vector< FAB_LAYER_COLOR > dummy
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
int GetTrailingInt(const wxString &aStr)
Gets the trailing int, if any, from a string.
wxString UnescapeString(const wxString &aSource)
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
bool operator()(const BOARD_ITEM *itemA, const BOARD_ITEM *itemB) const
Definition: footprint.cpp:3519
bool operator()(const PAD *aFirst, const PAD *aSecond) const
Definition: footprint.cpp:3596
bool operator()(const ZONE *aFirst, const ZONE *aSecond) const
Definition: footprint.cpp:3699
MANDATORY_FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ DATASHEET_FIELD
name of datasheet
@ FOOTPRINT_FIELD
Field Name Module PCB, i.e. "16DIP300".
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ DESCRIPTION_FIELD
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
constexpr int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_CENTER
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
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition: typeinfo.h:249
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition: typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:103
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition: typeinfo.h:99
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ 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_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:100
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition: typeinfo.h:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:104
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691