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