KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2015 Wayne Stambaugh <[email protected]>
7 * Copyright 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, see <https://www.gnu.org/licenses/>.
21 */
22
23#include "footprint.h"
24
25#include <magic_enum.hpp>
26
27#include <algorithm>
28#include <cmath>
29#include <unordered_set>
30#include <utility> // std::as_const
31
32#include <wx/log.h>
33#include <wx/debug.h>
34#include <wx/tokenzr.h>
35
36#include <bitmaps.h>
37#include <board.h>
39#include <collectors.h>
41#include <confirm.h>
46#include <core/kicad_algo.h>
48#include <embedded_files.h>
49#include <font/font.h>
50#include <font/outline_font.h>
54#include <i18n_utility.h>
55#include <lset.h>
56#include <macros.h>
57#include <pad.h>
58#include <pcb_dimension.h>
59#include <pcb_edit_frame.h>
60#include <pcb_field.h>
61#include <pcb_group.h>
63#include <pcb_marker.h>
64#include <pcb_point.h>
65#include <pcb_3d_body.h>
66#include <pcb_reference_image.h>
67#include <pcb_textbox.h>
68#include <pcb_track.h>
69#include <pcb_barcode.h>
70#include <name_validation.h>
71#include <refdes_utils.h>
72#include <string_utils.h>
73#include <view/view.h>
74#include <zone.h>
75
76#include <google/protobuf/any.pb.h>
77#include <api/board/board_types.pb.h>
78#include <api/api_enums.h>
79#include <api/api_utils.h>
80#include <api/api_pcb_utils.h>
81#include <properties/property.h>
83
84
86{
87public:
88 PCB_FOOTPRINT_FIELD_PROPERTY( const wxString& aName ) :
89 PROPERTY_BASE( aName ),
90 m_name( aName )
91 {
92 SetGroup( _HKI( "Fields" ) );
93 }
94
95 size_t OwnerHash() const override { return TYPE_HASH( FOOTPRINT ); }
96 size_t BaseHash() const override { return TYPE_HASH( FOOTPRINT ); }
97 size_t TypeHash() const override { return TYPE_HASH( wxString ); }
98
99 void setter( void* obj, wxAny& v ) override
100 {
101 wxString value;
102
103 if( !v.GetAs( &value ) )
104 return;
105
106 FOOTPRINT* footprint = reinterpret_cast<FOOTPRINT*>( obj );
107 PCB_FIELD* field = footprint->GetField( m_name );
108
109 wxString variantName;
110
111 if( footprint->GetBoard() )
112 variantName = footprint->GetBoard()->GetCurrentVariant();
113
114 if( !variantName.IsEmpty() )
115 {
116 FOOTPRINT_VARIANT* variant = footprint->AddVariant( variantName );
117
118 if( variant )
119 variant->SetFieldValue( m_name, value );
120 }
121 else if( !field )
122 {
123 PCB_FIELD* newField = new PCB_FIELD( footprint, FIELD_T::USER, m_name );
124 newField->SetText( value );
125 footprint->Add( newField );
126 }
127 else
128 {
129 field->SetText( value );
130 }
131 }
132
133 wxAny getter( const void* obj ) const override
134 {
135 const FOOTPRINT* footprint = reinterpret_cast<const FOOTPRINT*>( obj );
136 PCB_FIELD* field = footprint->GetField( m_name );
137
138 if( !field )
139 return wxAny();
140
141 wxString variantName;
142
143 if( footprint->GetBoard() )
144 variantName = footprint->GetBoard()->GetCurrentVariant();
145
146 wxString text;
147
148 if( !variantName.IsEmpty() )
149 text = footprint->GetFieldValueForVariant( variantName, m_name );
150 else
151 text = field->GetText();
152
153 return wxAny( text );
154 }
155
156private:
157 wxString m_name;
158};
159
160
163 m_attributes( 0 ),
171 m_lastEditTime( 0 ),
172 m_arflag( 0 ),
173 m_link( 0 ),
174 m_initial_comments( nullptr ),
176{
177 m_layer = F_Cu;
178 m_embedFonts = false;
179
180 auto addField =
181 [this]( FIELD_T id, PCB_LAYER_ID layer, bool visible )
182 {
183 PCB_FIELD* field = new PCB_FIELD( this, id );
184 field->SetLayer( layer );
185 field->SetVisible( visible );
186 m_fields.push_back( field );
187 };
188
189 addField( FIELD_T::REFERENCE, F_SilkS, true );
190 addField( FIELD_T::VALUE, F_Fab, true );
191 addField( FIELD_T::DATASHEET, F_Fab, false );
192 addField( FIELD_T::DESCRIPTION, F_Fab, false );
193
194 m_3D_Drawings.clear();
195}
196
197
198FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
199 BOARD_ITEM_CONTAINER( aFootprint ),
200 EMBEDDED_FILES( aFootprint ),
202{
203 m_transform = aFootprint.m_transform;
204 m_flipped = aFootprint.m_flipped;
205 m_fpid = aFootprint.m_fpid;
206 m_attributes = aFootprint.m_attributes;
207 m_fpStatus = aFootprint.m_fpStatus;
209
210 m_geometry_cache.reset();
211
217
219 m_clearance = aFootprint.m_clearance;
223
224 m_stackupLayers = aFootprint.m_stackupLayers;
225 m_stackupMode = aFootprint.m_stackupMode;
226
228 m_keywords = aFootprint.m_keywords;
229 m_path = aFootprint.m_path;
230 m_sheetname = aFootprint.m_sheetname;
231 m_sheetfile = aFootprint.m_sheetfile;
232 m_filters = aFootprint.m_filters;
233 m_lastEditTime = aFootprint.m_lastEditTime;
234 m_arflag = 0;
235 m_link = aFootprint.m_link;
236 m_privateLayers = aFootprint.m_privateLayers;
237
238 m_3D_Drawings = aFootprint.m_3D_Drawings;
239 m_height = aFootprint.m_height;
240
241 for( PCB_3D_BODY* body : aFootprint.m_bodies )
242 Add( static_cast<PCB_3D_BODY*>( body->Clone() ), ADD_MODE::APPEND );
243
244 m_initial_comments = aFootprint.m_initial_comments ? new wxArrayString( *aFootprint.m_initial_comments )
245 : nullptr;
246
247 m_embedFonts = aFootprint.m_embedFonts;
248 m_variants = aFootprint.m_variants;
249
250 m_componentClassCacheProxy->SetStaticComponentClass(
252
253 std::map<EDA_ITEM*, EDA_ITEM*> ptrMap;
254
255 // Copy fields
256 for( PCB_FIELD* field : aFootprint.m_fields )
257 {
258 if( field->IsMandatory() )
259 {
260 PCB_FIELD* existingField = GetField( field->GetId() );
261 ptrMap[field] = existingField;
262 *existingField = *field;
263
264 // Assignment retains the constructor-generated KIID because m_Uuid is const
265 existingField->SetUuidDirect( field->m_Uuid );
266 existingField->SetParent( this );
267 }
268 else
269 {
270 PCB_FIELD* newField = static_cast<PCB_FIELD*>( field->Clone() );
271 ptrMap[field] = newField;
272 Add( newField );
273 }
274 }
275
276 // Copy pads
277 for( PAD* pad : aFootprint.Pads() )
278 {
279 PAD* newPad = static_cast<PAD*>( pad->Clone() );
280 ptrMap[ pad ] = newPad;
281 Add( newPad, ADD_MODE::APPEND ); // Append to ensure indexes are identical
282 }
283
284 // Copy zones
285 for( ZONE* zone : aFootprint.Zones() )
286 {
287 ZONE* newZone = static_cast<ZONE*>( zone->Clone() );
288 ptrMap[ zone ] = newZone;
289 Add( newZone, ADD_MODE::APPEND ); // Append to ensure indexes are identical
290
291 // Ensure the net info is OK and especially uses the net info list
292 // living in the current board
293 // Needed when copying a fp from fp editor that has its own board
294 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
295 newZone->SetNetCode( -1 );
296 }
297
298 // Copy drawings
299 for( BOARD_ITEM* item : aFootprint.GraphicalItems() )
300 {
301 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
302 ptrMap[ item ] = newItem;
303 Add( newItem, ADD_MODE::APPEND ); // Append to ensure indexes are identical
304 }
305
306 // Copy groups
307 for( PCB_GROUP* group : aFootprint.Groups() )
308 {
309 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
310 ptrMap[ group ] = newGroup;
311 Add( newGroup, ADD_MODE::APPEND ); // Append to ensure indexes are identical
312 }
313
314 // Copy constraints. Clone preserves the uuid, so each constraint's KIID members still
315 // resolve to the matching cloned items.
316 for( PCB_CONSTRAINT* constraint : aFootprint.Constraints() )
317 Add( static_cast<PCB_CONSTRAINT*>( constraint->Clone() ), ADD_MODE::APPEND );
318
319 for( PCB_POINT* point : aFootprint.Points() )
320 {
321 PCB_POINT* newPoint = static_cast<PCB_POINT*>( point->Clone() );
322 ptrMap[ point ] = newPoint;
323 Add( newPoint, ADD_MODE::APPEND ); // Append to ensure indexes are identical
324 }
325
326 // Rebuild groups
327 for( PCB_GROUP* group : aFootprint.Groups() )
328 {
329 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( ptrMap[ group ] );
330
331 newGroup->GetItems().clear();
332
333 for( EDA_ITEM* member : group->GetItems() )
334 {
335 if( ptrMap.count( member ) )
336 newGroup->AddItem( ptrMap[ member ] );
337 }
338 }
339
340 // Embedded files are inherited via the EMBEDDED_FILES copy constructor invoked in the
341 // member initializer list above; the underlying file payloads are reference-counted so
342 // cloning a footprint is cheap even when it carries large embedded models or fonts.
343}
344
345
347 BOARD_ITEM_CONTAINER( aFootprint ),
349{
350 *this = std::move( aFootprint );
351}
352
353
355{
356 // Clean up the owned elements
357 delete m_initial_comments;
358
359 for( PCB_FIELD* f : m_fields )
360 delete f;
361
362 m_fields.clear();
363
364 for( PAD* p : m_pads )
365 delete p;
366
367 m_pads.clear();
368
369 for( ZONE* zone : m_zones )
370 delete zone;
371
372 m_zones.clear();
373
374 for( PCB_GROUP* group : m_groups )
375 delete group;
376
377 m_groups.clear();
378
379 for( PCB_CONSTRAINT* constraint : m_constraints )
380 delete constraint;
381
382 m_constraints.clear();
383
384 for( PCB_POINT* point : m_points )
385 delete point;
386
387 m_points.clear();
388
389 for( BOARD_ITEM* d : m_drawings )
390 delete d;
391
392 m_drawings.clear();
393
394 for( PCB_3D_BODY* body : m_bodies )
395 delete body;
396}
397
398std::vector<PROPERTY_BASE*> FOOTPRINT::GetDynamicProperties() const
399{
400 std::vector<PROPERTY_BASE*> props;
401 const BOARD* board = GetBoard();
402 bool isFPedit = board && board->IsFootprintHolder();
403
404 auto getOrCreate =
405 [&]( const wxString& aName )
406 {
407 auto it = m_fieldPropsStorageCache.find( aName );
408
409 if( it == m_fieldPropsStorageCache.end() )
410 {
411 auto prop = std::make_unique<PCB_FOOTPRINT_FIELD_PROPERTY>( aName );
412 it = m_fieldPropsStorageCache.emplace( aName, std::move( prop ) ).first;
413 }
414
415 return it->second.get();
416 };
417
418 for( PCB_FIELD* field : GetFields() )
419 {
420 if( !field->IsMandatory() )
421 continue;
422
423 if( !isFPedit && field->IsPrivate() )
424 continue;
425
426 const wxString& name = field->GetUntranslatedName();
427
428 if( PROPERTY_MANAGER::Instance().GetProperty( TYPE_HASH( FOOTPRINT ), name ) )
429 continue;
430
431 props.push_back( getOrCreate( name ) );
432 }
433
434 std::vector<PCB_FIELD*> userFields;
435
436 for( PCB_FIELD* field : GetFields() )
437 {
438 if( field->IsMandatory() || ( !isFPedit && field->IsPrivate() ) )
439 continue;
440
441 userFields.push_back( field );
442 }
443
444 std::ranges::sort( userFields,
445 []( const PCB_FIELD* a, const PCB_FIELD* b )
446 {
447 return a->GetUntranslatedName().CmpNoCase( b->GetUntranslatedName() ) < 0;
448 } );
449
450 for( PCB_FIELD* field : userFields )
451 {
452 const wxString& name = field->GetUntranslatedName();
453 props.push_back( getOrCreate( name ) );
454 }
455
457 props.push_back( prop );
458
459 return props;
460}
461
462
463void FOOTPRINT::Serialize( google::protobuf::Any &aContainer ) const
464{
465 using namespace kiapi::board;
466 types::FootprintInstance footprint;
467
468 footprint.mutable_id()->set_value( m_Uuid.AsStdString() );
469 footprint.mutable_position()->set_x_nm( GetPosition().x );
470 footprint.mutable_position()->set_y_nm( GetPosition().y );
471 footprint.mutable_orientation()->set_value_degrees( GetOrientationDegrees() );
472 footprint.set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( GetLayer() ) );
473 footprint.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
474 : kiapi::common::types::LockedState::LS_UNLOCKED );
475
476 if( const BOARD* board = GetBoard() )
477 footprint.mutable_parent()->set_value( board->m_Uuid.AsStdString() );
478
479 GetField( FIELD_T::REFERENCE )->Serialize( *footprint.mutable_reference_field() );
480 GetField( FIELD_T::VALUE )->Serialize( *footprint.mutable_value_field() );
481 GetField( FIELD_T::DATASHEET )->Serialize( *footprint.mutable_datasheet_field() );
482 GetField( FIELD_T::DESCRIPTION )->Serialize( *footprint.mutable_description_field() );
483
484 types::FootprintAttributes* attrs = footprint.mutable_attributes();
485
486 attrs->set_not_in_schematic( IsBoardOnly() );
487 attrs->set_exclude_from_position_files( IsExcludedFromPosFiles() );
488 attrs->set_exclude_from_bill_of_materials( IsExcludedFromBOM() );
489 attrs->set_exclude_from_simulation( IsExcludedFromSim() );
490 attrs->set_exempt_from_courtyard_requirement( AllowMissingCourtyard() );
491 attrs->set_do_not_populate( IsDNP() );
492 attrs->set_allow_soldermask_bridges( AllowSolderMaskBridges() );
493
495 attrs->set_mounting_style( types::FootprintMountingStyle::FMS_THROUGH_HOLE );
496 else if( m_attributes & FP_SMD )
497 attrs->set_mounting_style( types::FootprintMountingStyle::FMS_SMD );
498 else
499 attrs->set_mounting_style( types::FootprintMountingStyle::FMS_UNSPECIFIED );
500
501 SerializeDefinition( footprint.mutable_definition() );
502
503 types::FootprintDesignRuleOverrides* overrides = footprint.mutable_overrides();
504
505 if( GetLocalClearance().has_value() )
506 overrides->mutable_copper_clearance()->set_value_nm( *GetLocalClearance() );
507
508 if( GetLocalSolderMaskMargin().has_value() )
509 overrides->mutable_solder_mask()->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() );
510
511 if( GetLocalSolderPasteMargin().has_value() )
512 overrides->mutable_solder_paste()->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() );
513
514 if( GetLocalSolderPasteMarginRatio().has_value() )
515 overrides->mutable_solder_paste()->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() );
516
517 overrides->set_zone_connection(
519
520 kiapi::common::PackSheetPath( *footprint.mutable_symbol_path(), m_path );
521
522 footprint.set_symbol_sheet_name( m_sheetname.ToUTF8() );
523 footprint.set_symbol_sheet_filename( m_sheetfile.ToUTF8() );
524 footprint.set_symbol_footprint_filters( m_filters.ToUTF8() );
525
526 kiapi::board::PackEmbeddedFiles( *footprint.mutable_embedded_files(), *this );
527
528 kiapi::common::PackCustomProperties( footprint.mutable_custom_properties(), *this );
529
530 for( const auto& [variantName, variant] : m_variants )
531 {
532 types::FootprintVariant* variantMsg = footprint.add_variants();
533 variantMsg->set_name( variantName.ToUTF8() );
534 variantMsg->set_do_not_populate( variant.GetDNP() );
535 variantMsg->set_exclude_from_bill_of_materials( variant.GetExcludedFromBOM() );
536 variantMsg->set_exclude_from_position_files( variant.GetExcludedFromPosFiles() );
537 variantMsg->set_exclude_from_simulation( variant.GetExcludedFromSim() );
538
539 for( const auto& [fieldName, fieldValue] : variant.GetFields() )
540 {
541 variantMsg->mutable_fields()->insert( { std::string( fieldName.ToUTF8() ),
542 std::string( fieldValue.ToUTF8() ) } );
543 }
544 }
545
546 for( const FP_UNIT_INFO& unit : m_unitInfo )
547 {
548 types::FootprintUnitInfo* unitMsg = footprint.add_symbol_units();
549 unitMsg->set_name( unit.m_unitName.ToUTF8() );
550
551 for( const wxString& pinNumber : unit.m_pins )
552 unitMsg->add_pin_numbers( pinNumber.ToUTF8() );
553 }
554
555 aContainer.PackFrom( footprint );
556}
557
558
559void FOOTPRINT::SerializeDefinition( kiapi::board::types::Footprint* aOutput ) const
560{
561 using namespace kiapi::board;
562
563 kiapi::common::PackLibId( aOutput->mutable_id(), GetFPID() );
564 // anchor?
565 aOutput->mutable_attributes()->set_description( GetLibDescription().ToUTF8() );
566 aOutput->mutable_attributes()->set_keywords( GetKeywords().ToUTF8() );
567
568 // TODO: serialize library mandatory fields
569
570 for( const wxString& group : GetNetTiePadGroups() )
571 {
572 types::NetTieDefinition* netTie = aOutput->add_net_ties();
573 wxStringTokenizer tokenizer( group, ", \t\r\n", wxTOKEN_STRTOK );
574
575 while( tokenizer.HasMoreTokens() )
576 netTie->add_pad_number( tokenizer.GetNextToken().ToUTF8() );
577 }
578
579 for( PCB_LAYER_ID layer : GetPrivateLayers().Seq() )
580 aOutput->add_private_layers( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
581
582 types::JumperSettings* jumpers = aOutput->mutable_jumpers();
583 jumpers->set_duplicate_names_are_jumpered( GetDuplicatePadNumbersAreJumpers() );
584
585 for( const JUMPER_GROUP& group : JumperPadGroups().GetAll() )
586 {
587 types::JumperGroup* jumperGroup = jumpers->add_groups();
588
589 for( const wxString& padName : group.GetNames() )
590 jumperGroup->add_pad_names( padName.ToUTF8() );
591 }
592
593 if( GetHeight() > 0 )
594 kiapi::common::PackDistance( *aOutput->mutable_height(), GetHeight() );
595
596 for( const PCB_FIELD* item : m_fields )
597 {
598 if( item->IsMandatory() )
599 continue;
600
601 google::protobuf::Any* itemMsg = aOutput->add_items();
602 item->Serialize( *itemMsg );
603 }
604
605 for( const PAD* item : Pads() )
606 {
607 google::protobuf::Any* itemMsg = aOutput->add_items();
608 item->Serialize( *itemMsg );
609 }
610
611 for( const BOARD_ITEM* item : GraphicalItems() )
612 {
613 google::protobuf::Any* itemMsg = aOutput->add_items();
614 item->Serialize( *itemMsg );
615 }
616
617 for( const PCB_POINT* item : Points() )
618 {
619 google::protobuf::Any* itemMsg = aOutput->add_items();
620 item->Serialize( *itemMsg );
621 }
622
623 for( const ZONE* item : Zones() )
624 {
625 google::protobuf::Any* itemMsg = aOutput->add_items();
626 item->Serialize( *itemMsg );
627 }
628
629 for( const PCB_3D_BODY* body : Bodies() )
630 {
631 if( body->GetExtruded()->m_height > 0 )
632 {
633 google::protobuf::Any* itemMsg = aOutput->add_items();
634 body->Serialize( *itemMsg );
635 }
636 }
637
638 for( const FP_3DMODEL& model : Models() )
639 {
640 google::protobuf::Any* itemMsg = aOutput->add_items();
641 types::Footprint3DModel modelMsg;
642 modelMsg.set_filename( model.m_Filename.ToUTF8() );
643 kiapi::common::PackVector3D( *modelMsg.mutable_scale(), model.m_Scale );
644 kiapi::common::PackVector3D( *modelMsg.mutable_rotation(), model.m_Rotation );
645 kiapi::common::PackVector3D( *modelMsg.mutable_offset(), model.m_Offset );
646 modelMsg.set_visible( model.m_Show );
647 modelMsg.set_opacity( model.m_Opacity );
648 itemMsg->PackFrom( modelMsg );
649 }
650}
651
652
653bool FOOTPRINT::DeserializeDefinition( const kiapi::board::types::Footprint& aInput )
654{
655 using namespace kiapi::board;
656
657 SetFPID( kiapi::common::UnpackLibId( aInput.id() ) );
658 // TODO: how should anchor be handled?
659 SetLibDescription( aInput.attributes().description() );
660 SetKeywords( aInput.attributes().keywords() );
661
662 // TODO: deserialize library mandatory fields
663
664 m_netTiePadGroups.clear();
665
666 for( const types::NetTieDefinition& netTieMsg : aInput.net_ties() )
667 {
668 wxString group;
669
670 for( const std::string& pad : netTieMsg.pad_number() )
671 group.Append( wxString::Format( wxT( "%s, " ), pad ) );
672
673 group.Trim();
674 AddNetTiePadGroup( group.BeforeLast( ',' ) );
675 }
676
677 SetDuplicatePadNumbersAreJumpers( aInput.jumpers().duplicate_names_are_jumpered() );
678
679 JUMPER_GROUP_SET& jumperGroups = JumperPadGroups();
680 jumperGroups.Clear();
681
682 for( const types::JumperGroup& groupMsg : aInput.jumpers().groups() )
683 {
684 std::set<wxString> padNames;
685
686 for( const std::string& padName : groupMsg.pad_names() )
687 padNames.insert( wxString::FromUTF8( padName ) );
688
689 jumperGroups.Add( std::move( padNames ) );
690 }
691
692 LSET privateLayers;
693
694 for( int layerMsg : aInput.private_layers() )
695 {
696 auto layer = FromProtoEnum<PCB_LAYER_ID, types::BoardLayer>( static_cast<types::BoardLayer>( layerMsg ) );
697
698 if( layer > UNDEFINED_LAYER )
699 privateLayers.set( layer );
700 }
701
702 SetPrivateLayers( privateLayers );
703
704 if( int height = kiapi::common::UnpackDistance( aInput.height() ); height > 0 )
705 SetHeight( height );
706
707 // Footprint items
708 for( PCB_FIELD* field : m_fields )
709 {
710 if( !field->IsMandatory() )
711 Remove( field );
712 }
713
714 // If this footprint is on a board, uncache all items before clearing
715 if( BOARD* board = GetBoard() )
716 board->UncacheChildrenById( *this );
717
718 Pads().clear();
719 GraphicalItems().clear();
720 Zones().clear();
721 Groups().clear();
722 Constraints().clear();
723 Models().clear();
724 Points().clear();
725
726 for( PCB_3D_BODY* body : m_bodies )
727 Remove( body );
728
729 for( const google::protobuf::Any& itemMsg : aInput.items() )
730 {
731 std::optional<KICAD_T> type = kiapi::common::TypeNameFromAny( itemMsg );
732
733 if( !type )
734 {
735 // Bit of a hack here, but eventually 3D models should be promoted to a first-class
736 // object, at which point they can get their own serialization
737 if( itemMsg.type_url() == "type.googleapis.com/kiapi.board.types.Footprint3DModel" )
738 {
739 types::Footprint3DModel modelMsg;
740
741 if( !itemMsg.UnpackTo( &modelMsg ) )
742 continue;
743
745
746 model.m_Filename = wxString::FromUTF8( modelMsg.filename() );
747 model.m_Show = modelMsg.visible();
748 model.m_Opacity = modelMsg.opacity();
749 model.m_Scale = kiapi::common::UnpackVector3D( modelMsg.scale() );
750 model.m_Rotation = kiapi::common::UnpackVector3D( modelMsg.rotation() );
751 model.m_Offset = kiapi::common::UnpackVector3D( modelMsg.offset() );
752
753 Models().push_back( std::move( model ) );
754 }
755 else
756 {
757 wxLogTrace( traceApi, wxString::Format( wxS( "Attempting to unpack unknown type %s "
758 "from footprint message, skipping" ),
759 itemMsg.type_url() ) );
760 }
761
762 continue;
763 }
764
765 std::unique_ptr<BOARD_ITEM> item = CreateItemForType( *type, this );
766
767 if( item && item->Deserialize( itemMsg ) )
768 Add( item.release(), ADD_MODE::APPEND );
769 }
770
771 return true;
772}
773
774
775bool FOOTPRINT::Deserialize( const google::protobuf::Any &aContainer )
776{
777 using namespace kiapi::board;
778 types::FootprintInstance footprint;
779
780 if( !aContainer.UnpackTo( &footprint ) )
781 return false;
782
783 SetUuidDirect( KIID( footprint.id().value() ) );
784 SetPosition( VECTOR2I( footprint.position().x_nm(), footprint.position().y_nm() ) );
785 SetOrientationDegrees( footprint.orientation().value_degrees() );
787 SetLocked( footprint.locked() == kiapi::common::types::LockedState::LS_LOCKED );
788
789 google::protobuf::Any buf;
790 types::Field mandatoryField;
791
792 if( footprint.has_reference_field() )
793 {
794 mandatoryField = footprint.reference_field();
795 mandatoryField.mutable_id()->set_id( (int) FIELD_T::REFERENCE );
796 buf.PackFrom( mandatoryField );
798 }
799
800 if( footprint.has_value_field() )
801 {
802 mandatoryField = footprint.value_field();
803 mandatoryField.mutable_id()->set_id( (int) FIELD_T::VALUE );
804 buf.PackFrom( mandatoryField );
806 }
807
808 if( footprint.has_datasheet_field() )
809 {
810 mandatoryField = footprint.datasheet_field();
811 mandatoryField.mutable_id()->set_id( (int) FIELD_T::DATASHEET );
812 buf.PackFrom( mandatoryField );
814 }
815
816 if( footprint.has_description_field() )
817 {
818 mandatoryField = footprint.description_field();
819 mandatoryField.mutable_id()->set_id( (int) FIELD_T::DESCRIPTION );
820 buf.PackFrom( mandatoryField );
822 }
823
824 m_attributes = 0;
825
826 switch( footprint.attributes().mounting_style() )
827 {
828 case types::FootprintMountingStyle::FMS_THROUGH_HOLE:
830 break;
831
832 case types::FootprintMountingStyle::FMS_SMD:
834 break;
835
836 default:
837 break;
838 }
839
840 SetBoardOnly( footprint.attributes().not_in_schematic() );
841 SetExcludedFromBOM( footprint.attributes().exclude_from_bill_of_materials() );
842 SetExcludedFromSim( footprint.attributes().exclude_from_simulation() );
843 SetExcludedFromPosFiles( footprint.attributes().exclude_from_position_files() );
844 SetAllowMissingCourtyard( footprint.attributes().exempt_from_courtyard_requirement() );
845 SetDNP( footprint.attributes().do_not_populate() );
846 SetAllowSolderMaskBridges( footprint.attributes().allow_soldermask_bridges() );
847
848 // Definition
849 DeserializeDefinition( footprint.definition() );
850
851 const types::FootprintDesignRuleOverrides& overrides = footprint.overrides();
852
853 if( overrides.has_copper_clearance() )
854 SetLocalClearance( overrides.copper_clearance().value_nm() );
855 else
856 SetLocalClearance( std::nullopt );
857
858 if( overrides.has_solder_mask() && overrides.solder_mask().has_solder_mask_margin() )
859 SetLocalSolderMaskMargin( overrides.solder_mask().solder_mask_margin().value_nm() );
860 else
861 SetLocalSolderMaskMargin( std::nullopt );
862
863 if( overrides.has_solder_paste() )
864 {
865 const types::SolderPasteOverrides& pasteSettings = overrides.solder_paste();
866
867 if( pasteSettings.has_solder_paste_margin() )
868 SetLocalSolderPasteMargin( pasteSettings.solder_paste_margin().value_nm() );
869 else
870 SetLocalSolderPasteMargin( std::nullopt );
871
872 if( pasteSettings.has_solder_paste_margin_ratio() )
873 SetLocalSolderPasteMarginRatio( pasteSettings.solder_paste_margin_ratio().value() );
874 else
875 SetLocalSolderPasteMarginRatio( std::nullopt );
876 }
877
878 SetLocalZoneConnection( FromProtoEnum<ZONE_CONNECTION>( overrides.zone_connection() ) );
879
880 m_path = kiapi::common::UnpackSheetPath( footprint.symbol_path() );
881 m_sheetname = wxString::FromUTF8( footprint.symbol_sheet_name() );
882 m_sheetfile = wxString::FromUTF8( footprint.symbol_sheet_filename() );
883 m_filters = wxString::FromUTF8( footprint.symbol_footprint_filters() );
884
885 kiapi::common::UnpackCustomProperties( footprint.custom_properties(), *this );
886
887 if( !kiapi::board::UnpackEmbeddedFiles( *this, footprint.embedded_files() ) )
888 return false;
889
890 m_variants.clear();
891
892 for( const types::FootprintVariant& variantMsg : footprint.variants() )
893 {
894 wxString variantName = wxString::FromUTF8( variantMsg.name() );
895
896 if( variantName.IsEmpty() || variantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
897 continue;
898
899 FOOTPRINT_VARIANT& variant = m_variants[variantName];
900 variant.SetName( variantName );
901 variant.SetDNP( variantMsg.do_not_populate() );
902 variant.SetExcludedFromBOM( variantMsg.exclude_from_bill_of_materials() );
903 variant.SetExcludedFromPosFiles( variantMsg.exclude_from_position_files() );
904 variant.SetExcludedFromSim( variantMsg.exclude_from_simulation() );
905
906 for( const auto& [fieldName, fieldValue] : variantMsg.fields() )
907 variant.SetFieldValue( wxString::FromUTF8( fieldName ), wxString::FromUTF8( fieldValue ) );
908 }
909
910 m_unitInfo.clear();
911
912 for( const types::FootprintUnitInfo& unitMsg : footprint.symbol_units() )
913 {
914 FP_UNIT_INFO unit;
915 unit.m_unitName = wxString::FromUTF8( unitMsg.name() );
916
917 for( const std::string& pinNumber : unitMsg.pin_numbers() )
918 unit.m_pins.emplace_back( wxString::FromUTF8( pinNumber ) );
919
920 m_unitInfo.push_back( std::move( unit ) );
921 }
922
923 return true;
924}
925
926
928{
929 for( PCB_FIELD* field : m_fields )
930 {
931 if( field->GetId() == aFieldType )
932 return field;
933 }
934
935 PCB_FIELD* field = new PCB_FIELD( this, aFieldType );
936 m_fields.push_back( field );
937
938 return field;
939}
940
941
942const PCB_FIELD* FOOTPRINT::GetField( FIELD_T aFieldType ) const
943{
944 for( const PCB_FIELD* field : m_fields )
945 {
946 if( field->GetId() == aFieldType )
947 return field;
948 }
949
950 return nullptr;
951}
952
953
954bool FOOTPRINT::HasField( const wxString& aFieldName ) const
955{
956 return GetField( aFieldName ) != nullptr;
957}
958
959
960PCB_FIELD* FOOTPRINT::GetField( const wxString& aFieldName ) const
961{
962 for( PCB_FIELD* field : m_fields )
963 {
964 if( field->GetName() == aFieldName )
965 return field;
966 }
967
968 return nullptr;
969}
970
971
972void FOOTPRINT::GetFields( std::vector<PCB_FIELD*>& aVector, bool aVisibleOnly ) const
973{
974 aVector.clear();
975
976 for( PCB_FIELD* field : m_fields )
977 {
978 if( aVisibleOnly )
979 {
980 if( !field->IsVisible() || field->GetText().IsEmpty() )
981 continue;
982 }
983
984 aVector.push_back( field );
985 }
986
987 std::sort( aVector.begin(), aVector.end(),
988 []( PCB_FIELD* lhs, PCB_FIELD* rhs )
989 {
990 return lhs->GetOrdinal() < rhs->GetOrdinal();
991 } );
992}
993
994
995void FOOTPRINT::UpdateFields( const std::vector<PCB_FIELD>& aFields, std::vector<PCB_FIELD*>& aAdded,
996 std::vector<PCB_FIELD*>& aDetached )
997{
998 std::deque<PCB_FIELD*> updatedFieldSet;
999 std::vector<wxString> assignedFields;
1000
1001 auto assignOrAdd =
1002 [&]( PCB_FIELD* existingField, const PCB_FIELD& sourceField )
1003 {
1004 if( existingField )
1005 {
1006 *existingField = sourceField;
1007 existingField->ClearEditFlags();
1008 existingField->SetParent( this );
1009 updatedFieldSet.push_back( existingField );
1010 assignedFields.push_back( sourceField.GetName() );
1011 }
1012 else
1013 {
1014 PCB_FIELD* destField = sourceField.CloneField();
1015 aAdded.push_back( destField );
1016 updatedFieldSet.push_back( destField );
1017 }
1018 };
1019
1020 for( const PCB_FIELD& field : aFields )
1021 {
1022 // The const overload returns nullptr for a missing mandatory field instead of quietly creating one
1023 if( field.IsMandatory() )
1024 assignOrAdd( const_cast<PCB_FIELD*>( std::as_const( *this ).GetField( field.GetId() ) ), field );
1025 else
1026 assignOrAdd( GetField( field.GetName() ), field );
1027 }
1028
1029 for( PCB_FIELD* field : m_fields )
1030 {
1031 if( !field->IsMandatory() && !alg::contains( assignedFields, field->GetName() ) )
1032 aDetached.push_back( field );
1033 }
1034
1035 // Add() and Remove() carry the board's item-by-id cache and the geometry caches with them
1036 for( PCB_FIELD* field : aDetached )
1037 Remove( field );
1038
1039 for( PCB_FIELD* field : aAdded )
1040 Add( field );
1041
1042 // Add() appends, so restore the order the caller asked for
1043 m_fields = std::move( updatedFieldSet );
1044
1046}
1047
1048
1050{
1051 int ordinal = 42; // Arbitrarily larger than any mandatory FIELD_T id
1052
1053 for( const PCB_FIELD* field : m_fields )
1054 ordinal = std::max( ordinal, field->GetOrdinal() + 1 );
1055
1056 return ordinal;
1057}
1058
1059
1060void FOOTPRINT::ApplyDefaultSettings( const BOARD& board, bool aStyleFields, bool aStyleText,
1061 bool aStyleShapes, bool aStyleDimensions, bool aStyleBarcodes )
1062{
1063 if( aStyleFields )
1064 {
1065 for( PCB_FIELD* field : m_fields )
1066 field->StyleFromSettings( board.GetDesignSettings(), true );
1067 }
1068
1069 for( BOARD_ITEM* item : m_drawings )
1070 {
1071 switch( item->Type() )
1072 {
1073 case PCB_TEXT_T:
1074 case PCB_TEXTBOX_T:
1075 if( aStyleText )
1076 item->StyleFromSettings( board.GetDesignSettings(), true );
1077
1078 break;
1079
1080 case PCB_SHAPE_T:
1081 if( aStyleShapes && !item->IsOnCopperLayer() )
1082 item->StyleFromSettings( board.GetDesignSettings(), true );
1083
1084 break;
1085
1086 case PCB_DIM_ALIGNED_T:
1087 case PCB_DIM_LEADER_T:
1088 case PCB_DIM_CENTER_T:
1089 case PCB_DIM_RADIAL_T:
1091 if( aStyleDimensions )
1092 item->StyleFromSettings( board.GetDesignSettings(), true );
1093
1094 break;
1095
1096 case PCB_BARCODE_T:
1097 if( aStyleBarcodes )
1098 item->StyleFromSettings( board.GetDesignSettings(), true );
1099
1100 break;
1101
1102 default:
1103 break;
1104 }
1105 }
1106}
1107
1108
1110{
1111 // replace null UUIDs if any by a valid uuid
1112 std::vector< BOARD_ITEM* > item_list;
1113
1114 for( PCB_FIELD* field : m_fields )
1115 item_list.push_back( field );
1116
1117 for( PAD* pad : m_pads )
1118 item_list.push_back( pad );
1119
1120 for( BOARD_ITEM* gr_item : m_drawings )
1121 item_list.push_back( gr_item );
1122
1123 // Note: one cannot fix null UUIDs inside the group, but it should not happen
1124 // because null uuids can be found in old footprints, therefore without group
1125 for( PCB_GROUP* group : m_groups )
1126 item_list.push_back( group );
1127
1128 // Probably not needed, because old fp do not have zones. But just in case.
1129 for( ZONE* zone : m_zones )
1130 item_list.push_back( zone );
1131
1132 // Ditto
1133 for( PCB_POINT* point : m_points )
1134 item_list.push_back( point );
1135
1136 bool changed = false;
1137
1138 for( BOARD_ITEM* item : item_list )
1139 {
1140 if( item->m_Uuid == niluuid )
1141 {
1142 item->ResetUuidDirect();
1143 changed = true;
1144 }
1145 }
1146
1147 return changed;
1148}
1149
1150
1152{
1153 BOARD_ITEM::operator=( aOther );
1154
1155 m_courtyard_cache.reset();
1156 m_geometry_cache.reset();
1157
1158 m_fpid = aOther.m_fpid;
1159 m_attributes = aOther.m_attributes;
1160 m_fpStatus = aOther.m_fpStatus;
1161 m_transform = aOther.m_transform;
1162 m_flipped = aOther.m_flipped;
1163 m_lastEditTime = aOther.m_lastEditTime;
1164 m_link = aOther.m_link;
1165 m_path = aOther.m_path;
1166 m_variants = std::move( aOther.m_variants );
1167
1168 m_clearance = aOther.m_clearance;
1169 m_solderMaskMargin = aOther.m_solderMaskMargin;
1170 m_solderPasteMargin = aOther.m_solderPasteMargin;
1171 m_solderPasteMarginRatio = aOther.m_solderPasteMarginRatio;
1172 m_zoneConnection = aOther.m_zoneConnection;
1173 m_netTiePadGroups = aOther.m_netTiePadGroups;
1174 m_duplicatePadNumbersAreJumpers = aOther.m_duplicatePadNumbersAreJumpers;
1175 m_jumperPadGroups = aOther.m_jumperPadGroups;
1176
1177 // If this footprint is on a board, uncache all items before deleting them
1178 if( BOARD* board = GetBoard() )
1179 board->UncacheChildrenById( *this );
1180
1181 // Move the fields
1182 for( PCB_FIELD* field : m_fields )
1183 delete field;
1184
1185 m_fields.clear();
1186
1187 for( PCB_FIELD* field : aOther.m_fields )
1188 Add( field );
1189
1190 aOther.m_fields.clear();
1191
1192 // Move the pads
1193 for( PAD* pad : m_pads )
1194 delete pad;
1195
1196 m_pads.clear();
1197
1198 for( PAD* pad : aOther.Pads() )
1199 Add( pad );
1200
1201 aOther.Pads().clear();
1202
1203 // Move the zones
1204 for( ZONE* zone : m_zones )
1205 delete zone;
1206
1207 m_zones.clear();
1208
1209 for( ZONE* item : aOther.Zones() )
1210 {
1211 Add( item );
1212
1213 // Ensure the net info is OK and especially uses the net info list
1214 // living in the current board
1215 // Needed when copying a fp from fp editor that has its own board
1216 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
1217 item->SetNetCode( -1 );
1218 }
1219
1220 aOther.Zones().clear();
1221
1222 // Move the drawings
1223 for( BOARD_ITEM* item : m_drawings )
1224 delete item;
1225
1226 m_drawings.clear();
1227
1228 for( BOARD_ITEM* item : aOther.GraphicalItems() )
1229 Add( item );
1230
1231 aOther.GraphicalItems().clear();
1232
1233 // Move the groups
1234 for( PCB_GROUP* group : m_groups )
1235 delete group;
1236
1237 m_groups.clear();
1238
1239 for( PCB_GROUP* group : aOther.Groups() )
1240 Add( group );
1241
1242 aOther.Groups().clear();
1243
1244 // Move the constraints
1245 for( PCB_CONSTRAINT* constraint : m_constraints )
1246 delete constraint;
1247
1248 m_constraints.clear();
1249
1250 for( PCB_CONSTRAINT* constraint : aOther.Constraints() )
1251 Add( constraint );
1252
1253 aOther.Constraints().clear();
1254
1255 // Move the points
1256 for( PCB_POINT* point : m_points )
1257 delete point;
1258
1259 m_points.clear();
1260
1261 for( PCB_POINT* point : aOther.Points() )
1262 Add( point );
1263
1264 aOther.Points().clear();
1265
1266 EMBEDDED_FILES::operator=( std::move( aOther ) );
1267
1268 // Copy auxiliary data
1269 m_3D_Drawings = aOther.m_3D_Drawings;
1270 m_height = aOther.m_height;
1271 m_libDescription = aOther.m_libDescription;
1272 m_keywords = aOther.m_keywords;
1273 m_privateLayers = aOther.m_privateLayers;
1274
1275 m_initial_comments = aOther.m_initial_comments;
1276
1277 m_componentClassCacheProxy->SetStaticComponentClass(
1278 aOther.m_componentClassCacheProxy->GetStaticComponentClass() );
1279
1280 // Clear the other item's containers since this is a move
1281
1282 for( PCB_3D_BODY* body : m_bodies )
1283 delete body;
1284
1285 m_bodies.clear();
1286
1287 for( PCB_3D_BODY* body : aOther.m_bodies )
1288 Add( body );
1289
1290 aOther.m_bodies.clear();
1291 aOther.m_fields.clear();
1292 aOther.Pads().clear();
1293 aOther.Zones().clear();
1294 aOther.GraphicalItems().clear();
1295 aOther.m_initial_comments = nullptr;
1296
1297 return *this;
1298}
1299
1300
1302{
1303 BOARD_ITEM::operator=( aOther );
1304
1305 m_courtyard_cache.reset();
1306 m_geometry_cache.reset();
1307
1308 m_fpid = aOther.m_fpid;
1309 m_attributes = aOther.m_attributes;
1310 m_fpStatus = aOther.m_fpStatus;
1311 m_transform = aOther.m_transform;
1312 m_flipped = aOther.m_flipped;
1314 m_link = aOther.m_link;
1315 m_path = aOther.m_path;
1316
1317 m_clearance = aOther.m_clearance;
1325 m_variants = aOther.m_variants;
1326
1327 // If this footprint is on a board, uncache all items before deleting them
1328 if( BOARD* board = GetBoard() )
1329 board->UncacheChildrenById( *this );
1330
1331 std::map<EDA_ITEM*, EDA_ITEM*> ptrMap;
1332
1333 // Copy fields
1334 for( PCB_FIELD* field : m_fields )
1335 delete field;
1336
1337 m_fields.clear();
1338
1339 for( PCB_FIELD* field : aOther.m_fields )
1340 {
1341 PCB_FIELD* newField = new PCB_FIELD( *field );
1342 ptrMap[field] = newField;
1343 Add( newField );
1344 }
1345
1346 // Copy pads
1347 for( PAD* pad : m_pads )
1348 delete pad;
1349
1350 m_pads.clear();
1351
1352 for( PAD* pad : aOther.Pads() )
1353 {
1354 PAD* newPad = new PAD( *pad );
1355 ptrMap[ pad ] = newPad;
1356 Add( newPad );
1357 }
1358
1359 // Copy zones
1360 for( ZONE* zone : m_zones )
1361 delete zone;
1362
1363 m_zones.clear();
1364
1365 for( ZONE* zone : aOther.Zones() )
1366 {
1367 ZONE* newZone = static_cast<ZONE*>( zone->Clone() );
1368 ptrMap[ zone ] = newZone;
1369 Add( newZone );
1370
1371 // Ensure the net info is OK and especially uses the net info list
1372 // living in the current board
1373 // Needed when copying a fp from fp editor that has its own board
1374 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
1375 newZone->SetNetCode( -1 );
1376 }
1377
1378 // Copy drawings
1379 for( BOARD_ITEM* item : m_drawings )
1380 delete item;
1381
1382 m_drawings.clear();
1383
1384 for( BOARD_ITEM* item : aOther.GraphicalItems() )
1385 {
1386 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
1387 ptrMap[ item ] = newItem;
1388 Add( newItem );
1389 }
1390
1391 // Copy groups
1392 for( PCB_GROUP* group : m_groups )
1393 delete group;
1394
1395 m_groups.clear();
1396
1397 for( PCB_GROUP* group : aOther.Groups() )
1398 {
1399 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
1400 newGroup->GetItems().clear();
1401
1402 for( EDA_ITEM* member : group->GetItems() )
1403 newGroup->AddItem( ptrMap[ member ] );
1404
1405 Add( newGroup );
1406 }
1407
1408 // Copy constraints. Members reference items by KIID, which Clone preserves, so the
1409 // copies still point at the matching cloned items by uuid (resolved on next use).
1410 for( PCB_CONSTRAINT* constraint : m_constraints )
1411 delete constraint;
1412
1413 m_constraints.clear();
1414
1415 for( PCB_CONSTRAINT* constraint : aOther.Constraints() )
1416 Add( static_cast<PCB_CONSTRAINT*>( constraint->Clone() ) );
1417
1418 // Copy points
1419 for( PCB_POINT* point : m_points )
1420 delete point;
1421
1422 m_points.clear();
1423
1424 for( PCB_POINT* point : aOther.Points() )
1425 {
1426 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( point->Clone() );
1427 ptrMap[ point ] = newItem;
1428 Add( newItem );
1429 }
1430
1431 // Copy auxiliary data
1433 m_height = aOther.m_height;
1434
1435 for( PCB_3D_BODY* body : aOther.m_bodies )
1436 Add( static_cast<PCB_3D_BODY*>( body->Clone() ), ADD_MODE::APPEND );
1437
1439 m_keywords = aOther.m_keywords;
1441
1443 new wxArrayString( *aOther.m_initial_comments ) : nullptr;
1444
1445 m_componentClassCacheProxy->SetStaticComponentClass(
1447
1448 EMBEDDED_FILES::operator=( aOther );
1449
1450 return *this;
1451}
1452
1453
1454void FOOTPRINT::CopyFrom( const BOARD_ITEM* aOther )
1455{
1456 wxCHECK( aOther && aOther->Type() == PCB_FOOTPRINT_T, /* void */ );
1457 *this = *static_cast<const FOOTPRINT*>( aOther );
1458
1459 for( PAD* pad : m_pads )
1460 pad->SetDirty();
1461}
1462
1463
1465{
1466 {
1467 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
1468 m_geometry_cache.reset();
1469 }
1470
1471 std::lock_guard<std::mutex> lock( m_courtyard_cache_mutex );
1472 m_courtyard_cache.reset();
1473}
1474
1475
1477{
1478 return HasFlag( COURTYARD_CONFLICT );
1479}
1480
1481
1482bool FOOTPRINT::IsWithinSchematicSheet( const KIID_PATH& aSheetPath ) const
1483{
1484 if( aSheetPath.empty() )
1485 return false;
1486
1487 // m_path is written by the netlist, which omits the root sheet the way PathAsString() does
1488 return m_path.size() >= aSheetPath.size() - 1
1489 && std::equal( aSheetPath.begin() + 1, aSheetPath.end(), m_path.begin() );
1490}
1491
1492
1493void FOOTPRINT::GetContextualTextVars( wxArrayString* aVars ) const
1494{
1495 aVars->push_back( wxT( "REFERENCE" ) );
1496 aVars->push_back( wxT( "VALUE" ) );
1497 aVars->push_back( wxT( "LAYER" ) );
1498 aVars->push_back( wxT( "FOOTPRINT_LIBRARY" ) );
1499 aVars->push_back( wxT( "FOOTPRINT_NAME" ) );
1500 aVars->push_back( wxT( "SHORT_NET_NAME(<pad_number>)" ) );
1501 aVars->push_back( wxT( "NET_NAME(<pad_number>)" ) );
1502 aVars->push_back( wxT( "NET_CLASS(<pad_number>)" ) );
1503 aVars->push_back( wxT( "PIN_NAME(<pad_number>)" ) );
1504 aVars->push_back( wxT( "EXCLUDE_FROM_BOM" ) );
1505 aVars->push_back( wxT( "EXCLUDE_FROM_BOARD" ) );
1506 aVars->push_back( wxT( "EXCLUDE_FROM_SIM" ) );
1507 aVars->push_back( wxT( "EXCLUDE_FROM_POS_FILES" ) );
1508 aVars->push_back( wxT( "DNP" ) );
1509}
1510
1511
1512bool FOOTPRINT::ResolveTextVar( wxString* token, int aDepth ) const
1513{
1514 wxString variant;
1515
1516 if( GetBoard() )
1517 variant = GetBoard()->GetCurrentVariant();
1518
1519 return ResolveTextVar( token, variant, aDepth );
1520}
1521
1522
1523bool FOOTPRINT::ResolveTextVar( wxString* token, const wxString& aVariantName, int aDepth ) const
1524{
1525 if( GetBoard() && GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
1526 return false;
1527
1528 wxString variant = aVariantName;
1529
1530 if( token->IsSameAs( wxT( "REFERENCE" ) ) )
1531 {
1532 if( const PCB_FIELD* reference = GetField( FIELD_T::REFERENCE ) )
1533 *token = reference->GetShownText( variant, INTERNAL, aDepth + 1 );
1534 else
1535 token->Clear();
1536
1537 return true;
1538 }
1539 else if( token->IsSameAs( wxT( "VALUE" ) ) )
1540 {
1541 if( const PCB_FIELD* value = GetField( FIELD_T::VALUE ) )
1542 *token = value->GetShownText( variant, INTERNAL, aDepth + 1 );
1543 else
1544 token->Clear();
1545
1546 return true;
1547 }
1548 else if( token->IsSameAs( wxT( "LAYER" ) ) )
1549 {
1550 *token = GetLayerName();
1551 return true;
1552 }
1553 else if( token->IsSameAs( wxT( "FOOTPRINT_LIBRARY" ) ) )
1554 {
1555 *token = m_fpid.GetUniStringLibNickname();
1556 return true;
1557 }
1558 else if( token->IsSameAs( wxT( "FOOTPRINT_NAME" ) ) )
1559 {
1560 *token = m_fpid.GetUniStringLibItemName();
1561 return true;
1562 }
1563 else if( token->StartsWith( wxT( "SHORT_NET_NAME(" ) )
1564 || token->StartsWith( wxT( "NET_NAME(" ) )
1565 || token->StartsWith( wxT( "NET_CLASS(" ) )
1566 || token->StartsWith( wxT( "PIN_NAME(" ) ) )
1567 {
1568 wxString padNumber = token->AfterFirst( '(' );
1569 padNumber = padNumber.BeforeLast( ')' );
1570
1571 for( PAD* pad : Pads() )
1572 {
1573 if( pad->GetNumber() == padNumber )
1574 {
1575 if( token->StartsWith( wxT( "SHORT_NET_NAME" ) ) )
1576 *token = pad->GetShortNetname();
1577 else if( token->StartsWith( wxT( "NET_NAME" ) ) )
1578 *token = pad->GetNetname();
1579 else if( token->StartsWith( wxT( "NET_CLASS" ) ) )
1580 *token = pad->GetNetClassName();
1581 else
1582 *token = pad->GetPinFunction();
1583
1584 return true;
1585 }
1586 }
1587 }
1588 else if( token->IsSameAs( wxT( "EXCLUDE_FROM_BOM" ) ) )
1589 {
1590 *token = wxEmptyString;
1591
1592 if( GetExcludedFromBOMForVariant( variant ) )
1593 *token = wxS( "Excluded from BOM" );
1594
1595 return true;
1596 }
1597 else if( token->IsSameAs( wxT( "EXCLUDE_FROM_POS_FILES" ) ) )
1598 {
1599 *token = wxEmptyString;
1600
1601 if( GetExcludedFromPosFilesForVariant( variant ) )
1602 *token = wxS( "Excluded from position files" );
1603
1604 return true;
1605 }
1606 else if( token->IsSameAs( wxT( "EXCLUDE_FROM_BOARD" ) ) )
1607 {
1608 // Footprints are never excluded from board by definition
1609 *token = wxEmptyString;
1610 return true;
1611 }
1612 else if( token->IsSameAs( wxT( "EXCLUDE_FROM_SIM" ) ) )
1613 {
1614 *token = wxEmptyString;
1615
1616 if( GetExcludedFromSimForVariant( variant ) )
1617 *token = wxS( "Excluded from simulation" );
1618
1619 return true;
1620 }
1621 else if( token->IsSameAs( wxT( "DNP" ) ) )
1622 {
1623 *token = wxEmptyString;
1624
1625 if( GetDNPForVariant( variant ) )
1626 *token = wxS( "DNP" );
1627
1628 return true;
1629 }
1630 else if( PCB_FIELD* field = GetField( *token ) )
1631 {
1632 *token = field->GetShownText( variant, INTERNAL, aDepth + 1 );
1633 return true;
1634 }
1635 // The great property resolver: ${PROPERTY.My_Property}
1636 else if( token->StartsWith( wxS( "PROPERTY." ) ) )
1637 {
1638 // Get the second half, convert _ to ' '
1639 wxString propertyName = token->AfterFirst( '.' );
1640 propertyName.Replace( wxS( "_" ), wxS( " " ) );
1641
1642 // Check if the property manager knows this property
1644 PROPERTY_BASE* property = propMgr.GetProperty( this, propertyName );
1645
1646 if( !property || property->IsHiddenFromPropertiesManager() )
1647 return false;
1648
1649 if( !propMgr.IsAvailableFor( TYPE_HASH( *this ), property, const_cast<FOOTPRINT*>( this ) ) )
1650 return false;
1651
1652 KICAD_DIFF::DIFF_VALUE value = KICAD_DIFF::WxAnyToDiffValue( Get( property ), property );
1653
1655 return false;
1656
1657 *token = value.ToDisplayString( EDA_UNITS::MM, pcbIUScale );
1658 return true;
1659 }
1660
1661 if( GetBoard() && GetBoard()->ResolveTextVar( token, aVariantName, aDepth + 1 ) )
1662 return true;
1663
1664 return false;
1665}
1666
1667
1668// ============================================================================
1669// Variant Support Implementation
1670// ============================================================================
1671
1672const FOOTPRINT_VARIANT* FOOTPRINT::GetVariant( const wxString& aVariantName ) const
1673{
1674 auto it = m_variants.find( aVariantName );
1675
1676 return it != m_variants.end() ? &it->second : nullptr;
1677}
1678
1679
1680FOOTPRINT_VARIANT* FOOTPRINT::GetVariant( const wxString& aVariantName )
1681{
1682 auto it = m_variants.find( aVariantName );
1683
1684 return it != m_variants.end() ? &it->second : nullptr;
1685}
1686
1687
1689{
1690 if( aVariant.GetName().IsEmpty()
1691 || aVariant.GetName().CmpNoCase( GetDefaultVariantName() ) == 0 )
1692 {
1693 return;
1694 }
1695
1696 auto it = m_variants.find( aVariant.GetName() );
1697
1698 if( it != m_variants.end() )
1699 {
1700 FOOTPRINT_VARIANT updated = aVariant;
1701 updated.SetName( it->first );
1702 it->second = std::move( updated );
1703 return;
1704 }
1705
1706 m_variants.emplace( aVariant.GetName(), aVariant );
1707}
1708
1709
1710FOOTPRINT_VARIANT* FOOTPRINT::AddVariant( const wxString& aVariantName )
1711{
1712 if( aVariantName.IsEmpty()
1713 || aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
1714 {
1715 wxASSERT_MSG( false, wxT( "Variant name cannot be empty or default." ) );
1716 return nullptr;
1717 }
1718
1719 auto it = m_variants.find( aVariantName );
1720
1721 if( it != m_variants.end() )
1722 return &it->second;
1723
1724 FOOTPRINT_VARIANT variant( aVariantName );
1725 variant.SetDNP( IsDNP() );
1729
1730 auto inserted = m_variants.emplace( aVariantName, std::move( variant ) );
1731 return &inserted.first->second;
1732}
1733
1734
1735void FOOTPRINT::DeleteVariant( const wxString& aVariantName )
1736{
1737 m_variants.erase( aVariantName );
1738}
1739
1740
1741void FOOTPRINT::RenameVariant( const wxString& aOldName, const wxString& aNewName )
1742{
1743 if( aNewName.IsEmpty()
1744 || aNewName.CmpNoCase( GetDefaultVariantName() ) == 0 )
1745 {
1746 return;
1747 }
1748
1749 auto it = m_variants.find( aOldName );
1750
1751 if( it == m_variants.end() )
1752 return;
1753
1754 auto existingIt = m_variants.find( aNewName );
1755
1756 if( existingIt != m_variants.end() && existingIt != it )
1757 return;
1758
1759 if( it->first == aNewName )
1760 return;
1761
1762 FOOTPRINT_VARIANT variant = it->second;
1763 variant.SetName( aNewName );
1764 m_variants.erase( it );
1765 m_variants.emplace( aNewName, std::move( variant ) );
1766}
1767
1768
1769bool FOOTPRINT::HasVariant( const wxString& aVariantName ) const
1770{
1771 return m_variants.find( aVariantName ) != m_variants.end();
1772}
1773
1774
1775bool FOOTPRINT::GetDNPForVariant( const wxString& aVariantName ) const
1776{
1777 // Empty variant name means default
1778 if( aVariantName.IsEmpty() || aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
1779 return IsDNP();
1780
1781 const FOOTPRINT_VARIANT* variant = GetVariant( aVariantName );
1782
1783 if( variant )
1784 return variant->GetDNP();
1785
1786 // Fall back to default if variant doesn't exist
1787 return IsDNP();
1788}
1789
1790
1791bool FOOTPRINT::GetExcludedFromBOMForVariant( const wxString& aVariantName ) const
1792{
1793 // Empty variant name means default
1794 if( aVariantName.IsEmpty() || aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
1795 return IsExcludedFromBOM();
1796
1797 const FOOTPRINT_VARIANT* variant = GetVariant( aVariantName );
1798
1799 if( variant )
1800 return variant->GetExcludedFromBOM();
1801
1802 // Fall back to default if variant doesn't exist
1803 return IsExcludedFromBOM();
1804}
1805
1806
1807bool FOOTPRINT::GetExcludedFromSimForVariant( const wxString& aVariantName ) const
1808{
1809 // Empty variant name means default
1810 if( aVariantName.IsEmpty() || aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
1811 return IsExcludedFromSim();
1812
1813 const FOOTPRINT_VARIANT* variant = GetVariant( aVariantName );
1814
1815 if( variant )
1816 return variant->GetExcludedFromSim();
1817
1818 // Fall back to default if variant doesn't exist
1819 return IsExcludedFromSim();
1820}
1821
1822
1823bool FOOTPRINT::GetExcludedFromPosFilesForVariant( const wxString& aVariantName ) const
1824{
1825 // Empty variant name means default
1826 if( aVariantName.IsEmpty() || aVariantName.CmpNoCase( GetDefaultVariantName() ) == 0 )
1827 return IsExcludedFromPosFiles();
1828
1829 const FOOTPRINT_VARIANT* variant = GetVariant( aVariantName );
1830
1831 if( variant )
1832 return variant->GetExcludedFromPosFiles();
1833
1834 // Fall back to default if variant doesn't exist
1835 return IsExcludedFromPosFiles();
1836}
1837
1838
1839wxString FOOTPRINT::GetFieldValueForVariant( const wxString& aVariantName, const wxString& aFieldName ) const
1840{
1841 // Check variant-specific override first
1842 if( !aVariantName.IsEmpty() && aVariantName.CmpNoCase( GetDefaultVariantName() ) != 0 )
1843 {
1844 const FOOTPRINT_VARIANT* variant = GetVariant( aVariantName );
1845
1846 if( variant && variant->HasFieldValue( aFieldName ) )
1847 return variant->GetFieldValue( aFieldName );
1848 }
1849
1850 // Fall back to default field value
1851 if( const PCB_FIELD* field = GetField( aFieldName ) )
1852 return field->GetText();
1853
1854 return wxString();
1855}
1856
1857
1859{
1860 // Force the ORPHANED dummy net info on every BOARD_CONNECTED_ITEM descendant so that
1861 // operations which read through m_netinfo (e.g. library serialization) cannot chase a
1862 // dangling pointer when this footprint has been detached from its original parent board.
1863 // ORPHANED dummy net does not depend on a board.
1865 []( BOARD_ITEM* aItem )
1866 {
1867 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem ) )
1868 bci->SetNetCode( NETINFO_LIST::ORPHANED, /* aNoAssert */ true );
1869 },
1871}
1872
1873
1874void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
1875{
1876 switch( aBoardItem->Type() )
1877 {
1878 case PCB_FIELD_T:
1879 m_fields.push_back( static_cast<PCB_FIELD*>( aBoardItem ) );
1880 break;
1881
1882 case PCB_BARCODE_T:
1883 case PCB_TEXT_T:
1884 case PCB_DIM_ALIGNED_T:
1885 case PCB_DIM_LEADER_T:
1886 case PCB_DIM_CENTER_T:
1887 case PCB_DIM_RADIAL_T:
1889 case PCB_SHAPE_T:
1890 case PCB_TEXTBOX_T:
1891 case PCB_TABLE_T:
1893 if( aMode == ADD_MODE::APPEND )
1894 m_drawings.push_back( aBoardItem );
1895 else
1896 m_drawings.push_front( aBoardItem );
1897
1898 break;
1899
1900 case PCB_PAD_T:
1901 if( aMode == ADD_MODE::APPEND )
1902 m_pads.push_back( static_cast<PAD*>( aBoardItem ) );
1903 else
1904 m_pads.push_front( static_cast<PAD*>( aBoardItem ) );
1905
1906 break;
1907
1908 case PCB_ZONE_T:
1909 if( aMode == ADD_MODE::APPEND )
1910 m_zones.push_back( static_cast<ZONE*>( aBoardItem ) );
1911 else
1912 m_zones.insert( m_zones.begin(), static_cast<ZONE*>( aBoardItem ) );
1913
1914 break;
1915
1916 case PCB_GROUP_T:
1917 if( aMode == ADD_MODE::APPEND )
1918 m_groups.push_back( static_cast<PCB_GROUP*>( aBoardItem ) );
1919 else
1920 m_groups.insert( m_groups.begin(), static_cast<PCB_GROUP*>( aBoardItem ) );
1921
1922 break;
1923
1924 case PCB_CONSTRAINT_T:
1925 if( aMode == ADD_MODE::APPEND )
1926 m_constraints.push_back( static_cast<PCB_CONSTRAINT*>( aBoardItem ) );
1927 else
1928 m_constraints.insert( m_constraints.begin(), static_cast<PCB_CONSTRAINT*>( aBoardItem ) );
1929
1930 break;
1931
1932 case PCB_MARKER_T:
1933 wxFAIL_MSG( wxT( "FOOTPRINT::Add(): Markers go at the board level, even in the footprint editor" ) );
1934 return;
1935
1936 case PCB_FOOTPRINT_T:
1937 wxFAIL_MSG( wxT( "FOOTPRINT::Add(): Nested footprints not supported" ) );
1938 return;
1939
1940 case PCB_POINT_T:
1941 if( aMode == ADD_MODE::APPEND )
1942 m_points.push_back( static_cast<PCB_POINT*>( aBoardItem ) );
1943 else
1944 m_points.insert( m_points.begin(), static_cast<PCB_POINT*>( aBoardItem ) );
1945
1946 break;
1947
1948 case PCB_3D_BODY_T:
1949 if( aMode == ADD_MODE::APPEND )
1950 m_bodies.push_back( static_cast<PCB_3D_BODY*>( aBoardItem ) );
1951 else
1952 m_bodies.push_front( static_cast<PCB_3D_BODY*>( aBoardItem ) );
1953
1954 break;
1955
1956 default:
1957 wxFAIL_MSG( wxString::Format( wxT( "FOOTPRINT::Add(): BOARD_ITEM type (%d) not handled" ),
1958 aBoardItem->Type() ) );
1959
1960 return;
1961 }
1962
1963 aBoardItem->ClearEditFlags();
1964 aBoardItem->SetParent( this );
1965
1966 // If this footprint is on a board, update the board's item-by-id cache
1967 // Skip caching for copy-constructed footprints (inherited board ptr but not a real member).
1968 if( BOARD* board = GetBoard(); board && board->IsItemIndexedById( this ) )
1969 {
1970 board->CacheItemSubtreeById( *aBoardItem );
1971
1972 // A pad arriving here never passes through BOARD::Add, so this is the only chance to
1973 // invalidate the drill caches
1974 board->noteDrillModelChange( aBoardItem );
1975 }
1976
1978}
1979
1980
1981void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
1982{
1983 switch( aBoardItem->Type() )
1984 {
1985 case PCB_FIELD_T:
1986 for( auto it = m_fields.begin(); it != m_fields.end(); ++it )
1987 {
1988 if( *it == aBoardItem )
1989 {
1990 const wxString fieldName = ( *it )->GetUntranslatedName();
1991
1992 m_fields.erase( it );
1993
1994 for( auto& [variantName, footprintVariant] : m_variants )
1995 footprintVariant.RemoveFieldValue( fieldName );
1996
1997 break;
1998 }
1999 }
2000
2001 break;
2002
2003 case PCB_BARCODE_T:
2004 case PCB_TEXT_T:
2005 case PCB_DIM_ALIGNED_T:
2006 case PCB_DIM_CENTER_T:
2008 case PCB_DIM_RADIAL_T:
2009 case PCB_DIM_LEADER_T:
2010 case PCB_SHAPE_T:
2011 case PCB_TEXTBOX_T:
2012 case PCB_TABLE_T:
2014 for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
2015 {
2016 if( *it == aBoardItem )
2017 {
2018 m_drawings.erase( it );
2019 break;
2020 }
2021 }
2022
2023 break;
2024
2025 case PCB_PAD_T:
2026 for( auto it = m_pads.begin(); it != m_pads.end(); ++it )
2027 {
2028 if( *it == static_cast<PAD*>( aBoardItem ) )
2029 {
2030 m_pads.erase( it );
2031 break;
2032 }
2033 }
2034
2035 break;
2036
2037 case PCB_ZONE_T:
2038 for( auto it = m_zones.begin(); it != m_zones.end(); ++it )
2039 {
2040 if( *it == static_cast<ZONE*>( aBoardItem ) )
2041 {
2042 m_zones.erase( it );
2043 break;
2044 }
2045 }
2046
2047 break;
2048
2049 case PCB_CONSTRAINT_T:
2050 for( auto it = m_constraints.begin(); it != m_constraints.end(); ++it )
2051 {
2052 if( *it == static_cast<PCB_CONSTRAINT*>( aBoardItem ) )
2053 {
2054 m_constraints.erase( it );
2055 break;
2056 }
2057 }
2058
2059 break;
2060
2061 case PCB_GROUP_T:
2062 for( auto it = m_groups.begin(); it != m_groups.end(); ++it )
2063 {
2064 if( *it == static_cast<PCB_GROUP*>( aBoardItem ) )
2065 {
2066 m_groups.erase( it );
2067 break;
2068 }
2069 }
2070
2071 break;
2072
2073 case PCB_MARKER_T:
2074 wxFAIL_MSG( wxT( "FOOTPRINT::Remove(): Markers go at the board level, even in the footprint editor" ) );
2075 break;
2076
2077 case PCB_FOOTPRINT_T:
2078 wxFAIL_MSG( wxT( "FOOTPRINT::Remove(): Nested footprints not supported" ) );
2079 break;
2080
2081 case PCB_POINT_T:
2082 for( auto it = m_points.begin(); it != m_points.end(); ++it )
2083 {
2084 if( *it == static_cast<PCB_POINT*>( aBoardItem ) )
2085 {
2086 m_points.erase( it );
2087 break;
2088 }
2089 }
2090
2091 break;
2092
2093 case PCB_3D_BODY_T:
2094 std::erase( m_bodies, static_cast<PCB_3D_BODY*>( aBoardItem ) );
2095 break;
2096
2097 default:
2098 wxFAIL_MSG( wxString::Format( wxT( "FOOTPRINT::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
2099 aBoardItem->Type() ) );
2100 }
2101
2102 // If this footprint is on a board, update the board's item-by-id cache
2103 if( BOARD* board = GetBoard() )
2104 {
2105 if( board->IsItemIndexedById( this ) )
2106 {
2107 board->UncacheItemSubtreeById( *aBoardItem );
2108 board->noteDrillModelChange( aBoardItem );
2109 }
2110
2111 board->IncrementTimeStamp();
2112 }
2113
2114 aBoardItem->SetFlags( STRUCT_DELETED );
2115
2117}
2118
2119
2120double FOOTPRINT::GetArea( int aPadding ) const
2121{
2122 BOX2I bbox = GetBoundingBox( false );
2123
2124 double w = std::abs( static_cast<double>( bbox.GetWidth() ) ) + aPadding;
2125 double h = std::abs( static_cast<double>( bbox.GetHeight() ) ) + aPadding;
2126 return w * h;
2127}
2128
2129
2131{
2132 int smd_count = 0;
2133 int tht_count = 0;
2134
2135 for( PAD* pad : m_pads )
2136 {
2137 switch( pad->GetProperty() )
2138 {
2141 continue;
2142
2143 case PAD_PROP::HEATSINK:
2146 continue;
2147
2148 case PAD_PROP::NONE:
2149 case PAD_PROP::BGA:
2151 case PAD_PROP::PRESSFIT:
2152 break;
2153 }
2154
2155 switch( pad->GetAttribute() )
2156 {
2157 case PAD_ATTRIB::PTH:
2158 tht_count++;
2159 break;
2160
2161 case PAD_ATTRIB::SMD:
2162 if( pad->IsOnCopperLayer() )
2163 smd_count++;
2164
2165 break;
2166
2167 default:
2168 break;
2169 }
2170 }
2171
2172 // Footprints with plated through-hole pads should usually be marked through hole even if they
2173 // also have SMD because they might not be auto-placed. Exceptions to this might be shielded
2174 if( tht_count > 0 )
2175 return FP_THROUGH_HOLE;
2176
2177 if( smd_count > 0 )
2178 return FP_SMD;
2179
2180 return 0;
2181}
2182
2183
2185{
2187 return _( "SMD" );
2188
2190 return _( "Through hole" );
2191
2192 return _( "Unspecified" );
2193}
2194
2195
2206
2207
2209{
2211
2212 if( aMountingStyle == FOOTPRINT_TYPE::THROUGH_HOLE )
2214 else if( aMountingStyle == FOOTPRINT_TYPE::SMD )
2216}
2217
2218
2219std::vector<SEARCH_TERM>& FOOTPRINT::GetSearchTerms()
2220{
2221 m_searchTerms.clear();
2222 m_searchTerms.reserve( 6 );
2223
2224 m_searchTerms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
2225 m_searchTerms.emplace_back( SEARCH_TERM( GetName(), 8, true ) );
2226 m_searchTerms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16, true ) );
2227
2228 wxStringTokenizer keywordTokenizer( GetKeywords(), wxS( " \t\r\n" ), wxTOKEN_STRTOK );
2229
2230 while( keywordTokenizer.HasMoreTokens() )
2231 m_searchTerms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
2232
2233 m_searchTerms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
2234 m_searchTerms.emplace_back( SEARCH_TERM( GetLibDescription(), 1 ) );
2235
2236 return m_searchTerms;
2237}
2238
2239
2241{
2242 BOX2I bbox;
2243
2244 // We want the bounding box of the footprint pads at rot 0, not flipped
2245 // Create such a image:
2246 FOOTPRINT dummy( *this );
2247
2248 dummy.SetPosition( VECTOR2I( 0, 0 ) );
2249 dummy.SetOrientation( ANGLE_0 );
2250
2251 if( dummy.IsFlipped() )
2252 dummy.Flip( VECTOR2I( 0, 0 ), FLIP_DIRECTION::TOP_BOTTOM );
2253
2254 for( PAD* pad : dummy.Pads() )
2255 bbox.Merge( pad->GetBoundingBox() );
2256
2257 return bbox;
2258}
2259
2260
2262{
2263 for( BOARD_ITEM* item : m_drawings )
2264 {
2265 if( m_privateLayers.test( item->GetLayer() ) )
2266 continue;
2267
2268 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_TEXT_T )
2269 return false;
2270 }
2271
2272 return true;
2273}
2274
2275
2277{
2278 return GetBoundingBox( true );
2279}
2280
2281
2282const BOX2I FOOTPRINT::GetBoundingBox( bool aIncludeText ) const
2283{
2284 const BOARD* board = GetBoard();
2285
2286 if( board )
2287 {
2288 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
2289
2290 if( !m_geometry_cache )
2291 m_geometry_cache = std::make_unique<FOOTPRINT_GEOMETRY_CACHE_DATA>();
2292
2293 if( aIncludeText )
2294 {
2295 if( m_geometry_cache->bounding_box_timestamp >= board->GetTimeStamp() )
2296 return m_geometry_cache->bounding_box;
2297 }
2298 else
2299 {
2300 if( m_geometry_cache->text_excluded_bbox_timestamp >= board->GetTimeStamp() )
2301 return m_geometry_cache->text_excluded_bbox;
2302 }
2303 }
2304
2305 std::vector<PCB_TEXT*> texts;
2306 bool isFPEdit = board && board->IsFootprintHolder();
2307
2308 BOX2I bbox( m_transform.GetTranslate() );
2309 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Give a min size to the bbox
2310
2311 // Calculate the footprint side
2312 PCB_LAYER_ID footprintSide = GetSide();
2313
2314 for( BOARD_ITEM* item : m_drawings )
2315 {
2316 if( IsValidLayer( item->GetLayer() ) && m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
2317 continue;
2318
2319 // We want the bitmap bounding box just in the footprint editor
2320 // so it will start with the correct initial zoom
2321 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
2322 continue;
2323
2324 // Handle text separately
2325 if( item->Type() == PCB_TEXT_T )
2326 {
2327 texts.push_back( static_cast<PCB_TEXT*>( item ) );
2328 continue;
2329 }
2330
2331 // If we're not including text then drop annotations as well -- unless, of course, it's
2332 // an unsided footprint -- in which case it's likely to be nothing *but* annotations.
2333 if( !aIncludeText && footprintSide != UNDEFINED_LAYER )
2334 {
2335 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
2336 continue;
2337
2338 if( item->GetLayer() == Cmts_User || item->GetLayer() == Dwgs_User
2339 || item->GetLayer() == Eco1_User || item->GetLayer() == Eco2_User )
2340 {
2341 continue;
2342 }
2343 }
2344
2345 bbox.Merge( item->GetBoundingBox() );
2346 }
2347
2348 for( PCB_FIELD* field : m_fields )
2349 {
2350 // Reference and value get their own processing
2351 if( field->IsReference() || field->IsValue() )
2352 continue;
2353
2354 texts.push_back( field );
2355 }
2356
2357 for( PAD* pad : m_pads )
2358 bbox.Merge( pad->GetBoundingBox() );
2359
2360 for( ZONE* zone : m_zones )
2361 bbox.Merge( zone->GetBoundingBox() );
2362
2363 for( PCB_POINT* point : m_points )
2364 bbox.Merge( point->GetBoundingBox() );
2365
2366 bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_zones.empty() );
2367
2368 // Groups do not contribute to the rect, only their members
2369 if( aIncludeText || noDrawItems )
2370 {
2371 // Only PCB_TEXT and PCB_FIELD items are independently selectable; PCB_TEXTBOX items go
2372 // in with other graphic items above.
2373 for( PCB_TEXT* text : texts )
2374 {
2375 if( !isFPEdit && m_privateLayers.test( text->GetLayer() ) )
2376 continue;
2377
2378 if( text->Type() == PCB_FIELD_T && !text->IsVisible() )
2379 continue;
2380
2381 bbox.Merge( text->GetBoundingBox() );
2382 }
2383
2384 // A footprint is constructed with its mandatory fields, but they can be removed again
2385 // through the editing dialogs or the scripting API, and the const accessors return
2386 // nullptr rather than recreating them.
2387 const PCB_FIELD* value = GetField( FIELD_T::VALUE );
2388 const PCB_FIELD* reference = GetField( FIELD_T::REFERENCE );
2389
2390 // This can be further optimized when aIncludeInvisibleText is true, but currently
2391 // leaving this as is until it's determined there is a noticeable speed hit.
2392 bool valueLayerIsVisible = true;
2393 bool refLayerIsVisible = true;
2394
2395 if( board )
2396 {
2397 // The first "&&" conditional handles the user turning layers off as well as layers
2398 // not being present in the current PCB stackup. Values, references, and all
2399 // footprint text can also be turned off via the GAL meta-layers, so the 2nd and
2400 // 3rd "&&" conditionals handle that.
2401 if( value )
2402 {
2403 valueLayerIsVisible = board->IsLayerVisible( value->GetLayer() )
2404 && board->IsElementVisible( LAYER_FP_VALUES )
2405 && board->IsElementVisible( LAYER_FP_TEXT );
2406 }
2407
2408 if( reference )
2409 {
2410 refLayerIsVisible = board->IsLayerVisible( reference->GetLayer() )
2411 && board->IsElementVisible( LAYER_FP_REFERENCES )
2412 && board->IsElementVisible( LAYER_FP_TEXT );
2413 }
2414 }
2415
2416
2417 if( value && ( ( value->IsVisible() && valueLayerIsVisible ) || noDrawItems ) )
2418 bbox.Merge( value->GetBoundingBox() );
2419
2420 if( reference && ( ( reference->IsVisible() && refLayerIsVisible ) || noDrawItems ) )
2421 bbox.Merge( reference->GetBoundingBox() );
2422 }
2423
2424 if( board )
2425 {
2426 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
2427
2428 if( !m_geometry_cache )
2429 m_geometry_cache = std::make_unique<FOOTPRINT_GEOMETRY_CACHE_DATA>();
2430
2431 if( aIncludeText || noDrawItems )
2432 {
2433 m_geometry_cache->bounding_box_timestamp = board->GetTimeStamp();
2434 m_geometry_cache->bounding_box = bbox;
2435 }
2436 else
2437 {
2438 m_geometry_cache->text_excluded_bbox_timestamp = board->GetTimeStamp();
2439 m_geometry_cache->text_excluded_bbox = bbox;
2440 }
2441 }
2442
2443 return bbox;
2444}
2445
2446
2447const BOX2I FOOTPRINT::GetLayerBoundingBox( const LSET& aLayers ) const
2448{
2449 std::vector<PCB_TEXT*> texts;
2450 const BOARD* board = GetBoard();
2451 bool isFPEdit = board && board->IsFootprintHolder();
2452
2453 // Start with an uninitialized bounding box
2454 BOX2I bbox;
2455
2456 for( BOARD_ITEM* item : m_drawings )
2457 {
2458 if( IsValidLayer( item->GetLayer() ) && m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
2459 continue;
2460
2461 if( ( aLayers & item->GetLayerSet() ).none() )
2462 continue;
2463
2464 // We want the bitmap bounding box just in the footprint editor
2465 // so it will start with the correct initial zoom
2466 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
2467 continue;
2468
2469 bbox.Merge( item->GetBoundingBox() );
2470 }
2471
2472 for( PAD* pad : m_pads )
2473 {
2474 if( ( aLayers & pad->GetLayerSet() ).none() )
2475 continue;
2476
2477 bbox.Merge( pad->GetBoundingBox() );
2478 }
2479
2480 for( ZONE* zone : m_zones )
2481 {
2482 if( ( aLayers & zone->GetLayerSet() ).none() )
2483 continue;
2484
2485 bbox.Merge( zone->GetBoundingBox() );
2486 }
2487
2488 for( PCB_POINT* point : m_points )
2489 {
2490 if( m_privateLayers.test( point->GetLayer() ) && !isFPEdit )
2491 continue;
2492
2493 if( ( aLayers & point->GetLayerSet() ).none() )
2494 continue;
2495
2496 bbox.Merge( point->GetBoundingBox() );
2497 }
2498
2499 return bbox;
2500}
2501
2502
2504{
2505 const BOARD* board = GetBoard();
2506 bool isFPEdit = board && board->IsFootprintHolder();
2507
2508 if( board )
2509 {
2510 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
2511
2512 if( m_geometry_cache && m_geometry_cache->hull_timestamp >= board->GetTimeStamp() )
2513 return m_geometry_cache->hull;
2514 }
2515
2516 SHAPE_POLY_SET rawPolys;
2517
2518 for( BOARD_ITEM* item : m_drawings )
2519 {
2520 if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
2521 continue;
2522
2523 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_REFERENCE_IMAGE_T )
2524 item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2525
2526 // We intentionally exclude footprint fields from the bounding hull.
2527 }
2528
2529 for( PAD* pad : m_pads )
2530 {
2531 pad->Padstack().ForEachUniqueLayer(
2532 [&]( PCB_LAYER_ID aLayer )
2533 {
2534 pad->TransformShapeToPolygon( rawPolys, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2535 } );
2536
2537 // In case hole is larger than pad
2538 pad->TransformHoleToPolygon( rawPolys, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2539 }
2540
2541 for( ZONE* zone : m_zones )
2542 {
2543 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
2544 {
2545 const SHAPE_POLY_SET& layerPoly = *zone->GetFilledPolysList( layer );
2546
2547 for( int ii = 0; ii < layerPoly.OutlineCount(); ii++ )
2548 {
2549 const SHAPE_LINE_CHAIN& poly = layerPoly.COutline( ii );
2550 rawPolys.AddOutline( poly );
2551 }
2552 }
2553 }
2554
2555 // If there are some graphic items, build the actual hull.
2556 // However if no items, create a minimal polygon (can happen if a footprint
2557 // is created with no item: it contains only 2 texts.
2558 if( rawPolys.OutlineCount() == 0 || rawPolys.FullPointCount() < 3 )
2559 {
2560 // generate a small dummy rectangular outline around the anchor
2561 const int halfsize = pcbIUScale.mmToIU( 1.0 );
2562
2563 rawPolys.NewOutline();
2564
2565 // add a square:
2566 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
2567 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
2568 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y + halfsize );
2569 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y + halfsize );
2570 }
2571
2572 std::vector<VECTOR2I> convex_hull;
2573 BuildConvexHull( convex_hull, rawPolys );
2574
2575 {
2576 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
2577
2578 if( !m_geometry_cache )
2579 m_geometry_cache = std::make_unique<FOOTPRINT_GEOMETRY_CACHE_DATA>();
2580
2581 m_geometry_cache->hull.RemoveAllContours();
2582 m_geometry_cache->hull.NewOutline();
2583
2584 for( const VECTOR2I& pt : convex_hull )
2585 m_geometry_cache->hull.Append( pt );
2586
2587 if( board )
2588 m_geometry_cache->hull_timestamp = board->GetTimeStamp();
2589
2590 return m_geometry_cache->hull;
2591 }
2592}
2593
2594
2596{
2597 const BOARD* board = GetBoard();
2598 bool isFPEdit = board && board->IsFootprintHolder();
2599
2600 SHAPE_POLY_SET rawPolys;
2601 SHAPE_POLY_SET hull;
2602
2603 for( BOARD_ITEM* item : m_drawings )
2604 {
2605 if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
2606 continue;
2607
2608 if( item->IsOnLayer( aLayer ) )
2609 {
2610 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_REFERENCE_IMAGE_T )
2611 item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2612
2613 // We intentionally exclude footprint fields from the bounding hull.
2614 }
2615 }
2616
2617 for( PAD* pad : m_pads )
2618 {
2619 if( pad->IsOnLayer( aLayer ) )
2620 pad->TransformShapeToPolygon( rawPolys, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2621 }
2622
2623 for( ZONE* zone : m_zones )
2624 {
2625 if( zone->GetIsRuleArea() )
2626 continue;
2627
2628 if( zone->IsOnLayer( aLayer ) )
2629 {
2630 const std::shared_ptr<SHAPE_POLY_SET>& layerPoly = zone->GetFilledPolysList( aLayer );
2631
2632 for( int ii = 0; ii < layerPoly->OutlineCount(); ii++ )
2633 rawPolys.AddOutline( layerPoly->COutline( ii ) );
2634 }
2635 }
2636
2637 std::vector<VECTOR2I> convex_hull;
2638 BuildConvexHull( convex_hull, rawPolys );
2639
2640 hull.NewOutline();
2641
2642 for( const VECTOR2I& pt : convex_hull )
2643 hull.Append( pt );
2644
2645 return hull;
2646}
2647
2648
2649void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
2650{
2651 wxString msg, msg2;
2652 wxString variant;
2653
2654 if( BOARD* board = GetBoard() )
2655 variant = board->GetCurrentVariant();
2656
2657 // Don't use GetShownText(); we want to see the variable references here
2658 aList.emplace_back( UnescapeString( Reference().GetText() ),
2660 UNTRANSLATED ) ) ) );
2661
2662 if( aFrame->IsType( FRAME_FOOTPRINT_VIEWER )
2663 || aFrame->IsType( FRAME_FOOTPRINT_CHOOSER )
2664 || aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
2665 {
2666 aList.emplace_back( _( "Library" ), GetFPID().GetLibNickname().wx_str() );
2667
2668 aList.emplace_back( _( "Footprint Name" ), GetFPID().GetLibItemName().wx_str() );
2669
2670 aList.emplace_back( _( "Pads" ), wxString::Format( wxT( "%u" ), GetNumberedPadCount() ) );
2671
2672 aList.emplace_back( wxString::Format( _( "Doc: %s" ), GetLibDescription() ),
2673 wxString::Format( _( "Keywords: %s" ), GetKeywords() ) );
2674
2675 return;
2676 }
2677
2678 // aFrame is the board editor:
2679
2680 switch( GetSide() )
2681 {
2682 case F_Cu: aList.emplace_back( _( "Board Side" ), _( "Front" ) ); break;
2683 case B_Cu: aList.emplace_back( _( "Board Side" ), _( "Back (Flipped)" ) ); break;
2684 default: /* unsided: user-layers only, etc. */ break;
2685 }
2686
2687 aList.emplace_back( _( "Rotation" ), wxString::Format( wxT( "%.4g" ), GetOrientation().AsDegrees() ) );
2688
2689 auto addToken = []( wxString* aStr, const wxString& aAttr )
2690 {
2691 if( !aStr->IsEmpty() )
2692 *aStr += wxT( ", " );
2693
2694 *aStr += aAttr;
2695 };
2696
2697 wxString status;
2698 wxString attrs;
2699
2700 if( IsLocked() )
2701 addToken( &status, _( "Locked" ) );
2702
2703 if( IsPlaced() )
2704 addToken( &status, _( "autoplaced" ) );
2705
2706 if( IsBoardOnly() )
2707 addToken( &attrs, _( "not in schematic" ) );
2708
2709 if( GetExcludedFromPosFilesForVariant( variant ) )
2710 addToken( &attrs, _( "exclude from pos files" ) );
2711
2712 if( GetExcludedFromBOMForVariant( variant ) )
2713 addToken( &attrs, _( "exclude from BOM" ) );
2714
2715 if( GetExcludedFromSimForVariant( variant ) )
2716 addToken( &attrs, _( "exclude from simulation" ) );
2717
2718 if( GetDNPForVariant( variant ) )
2719 addToken( &attrs, _( "DNP" ) );
2720
2721 aList.emplace_back( _( "Status: " ) + status, _( "Attributes:" ) + wxS( " " ) + attrs );
2722
2723 if( !m_componentClassCacheProxy->GetComponentClass()->IsEmpty() )
2724 {
2725 aList.emplace_back( _( "Component Class" ),
2726 m_componentClassCacheProxy->GetComponentClass()->GetHumanReadableName() );
2727 }
2728
2729 msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
2730 msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" ) : m_3D_Drawings.front().m_Filename );
2731 aList.emplace_back( msg, msg2 );
2732
2733 msg.Printf( _( "Doc: %s" ), m_libDescription );
2734 msg2.Printf( _( "Keywords: %s" ), m_keywords );
2735 aList.emplace_back( msg, msg2 );
2736}
2737
2738
2740{
2741 if( const BOARD* board = GetBoard() )
2742 {
2743 if( board->IsFootprintHolder() )
2744 return UNDEFINED_LAYER;
2745 }
2746
2747 // Test pads first; they're the most likely to return a quick answer.
2748 for( PAD* pad : m_pads )
2749 {
2750 if( ( LSET::SideSpecificMask() & pad->GetLayerSet() ).any() )
2751 return GetLayer();
2752 }
2753
2754 for( BOARD_ITEM* item : m_drawings )
2755 {
2756 if( IsValidLayer( item->GetLayer() ) && LSET::SideSpecificMask().test( item->GetLayer() ) )
2757 return GetLayer();
2758 }
2759
2760 for( ZONE* zone : m_zones )
2761 {
2762 if( ( LSET::SideSpecificMask() & zone->GetLayerSet() ).any() )
2763 return GetLayer();
2764 }
2765
2766 return UNDEFINED_LAYER;
2767}
2768
2769
2771{
2772 auto isOnLayer =
2773 [aLayer]( const BOARD_ITEM* aItem )
2774 {
2775 return aItem->IsOnLayer( aLayer );
2776 };
2777
2778 return std::ranges::any_of( m_pads, isOnLayer )
2779 || std::ranges::any_of( m_zones, isOnLayer )
2780 || std::ranges::any_of( m_fields, isOnLayer )
2781 || std::ranges::any_of( m_drawings, isOnLayer );
2782}
2783
2784
2785bool FOOTPRINT::HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy ) const
2786{
2787 for( PAD* pad : m_pads )
2788 {
2789 if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition, aAccuracy ) )
2790 return true;
2791 }
2792
2793 for( ZONE* zone : m_zones )
2794 {
2795 if( zone->IsOnLayer( aLayer ) && zone->HitTest( aPosition, aAccuracy ) )
2796 return true;
2797 }
2798
2799 for( BOARD_ITEM* item : m_drawings )
2800 {
2801 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer )
2802 && item->HitTest( aPosition, aAccuracy ) )
2803 {
2804 return true;
2805 }
2806 }
2807
2808 return false;
2809}
2810
2811
2812bool FOOTPRINT::HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy ) const
2813{
2814 std::vector<BOARD_ITEM*> items;
2815
2816 for( PAD* pad : m_pads )
2817 {
2818 if( pad->IsOnLayer( aLayer ) )
2819 items.push_back( pad );
2820 }
2821
2822 for( ZONE* zone : m_zones )
2823 {
2824 if( zone->IsOnLayer( aLayer ) )
2825 items.push_back( zone );
2826 }
2827
2828 for( BOARD_ITEM* item : m_drawings )
2829 {
2830 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) )
2831 items.push_back( item );
2832 }
2833
2834 // If we require the elements to be contained in the rect and any of them are not,
2835 // we can return false;
2836 // Conversely, if we just require any of the elements to have a hit, we can return true
2837 // when the first one is found.
2838 for( BOARD_ITEM* item : items )
2839 {
2840 if( !aContained && item->HitTest( aRect, aContained, aAccuracy ) )
2841 return true;
2842 else if( aContained && !item->HitTest( aRect, aContained, aAccuracy ) )
2843 return false;
2844 }
2845
2846 // If we didn't exit in the loop, that means that we did not return false for aContained or
2847 // we did not return true for !aContained. So we can just return the bool with a test of
2848 // whether there were any elements or not.
2849 return !items.empty() && aContained;
2850}
2851
2852
2853bool FOOTPRINT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
2854{
2855 BOX2I rect = GetBoundingBox( false );
2856 return rect.Inflate( aAccuracy ).Contains( aPosition );
2857}
2858
2859
2860bool FOOTPRINT::HitTestAccurate( const VECTOR2I& aPosition, int aAccuracy ) const
2861{
2862 return GetBoundingHull().Collide( aPosition, aAccuracy );
2863}
2864
2865
2866bool FOOTPRINT::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
2867{
2868 BOX2I arect = aRect;
2869 arect.Inflate( aAccuracy );
2870
2871 if( aContained )
2872 {
2873 return arect.Contains( GetBoundingBox( false ) );
2874 }
2875 else
2876 {
2877 // If the rect does not intersect the bounding box, skip any tests
2878 if( !arect.Intersects( GetBoundingBox( false ) ) )
2879 return false;
2880
2881 // If there are no pads, zones, or drawings, allow intersection with text
2882 if( m_pads.empty() && m_zones.empty() && m_drawings.empty() )
2883 return GetBoundingBox( true ).Intersects( arect );
2884
2885 // Determine if any elements in the FOOTPRINT intersect the rect
2886 for( PAD* pad : m_pads )
2887 {
2888 if( pad->HitTest( arect, false, 0 ) )
2889 return true;
2890 }
2891
2892 for( ZONE* zone : m_zones )
2893 {
2894 if( zone->HitTest( arect, false, 0 ) )
2895 return true;
2896 }
2897
2898 for( PCB_POINT* point : m_points )
2899 {
2900 if( point->HitTest( arect, false, 0 ) )
2901 return true;
2902 }
2903
2904 // PCB fields are selectable on their own, so they don't get tested
2905
2906 for( BOARD_ITEM* item : m_drawings )
2907 {
2908 // Text items are selectable on their own, and are therefore excluded from this
2909 // test. TextBox items are NOT selectable on their own, and so MUST be included
2910 // here. Bitmaps aren't selectable since they aren't displayed.
2911 if( item->Type() != PCB_TEXT_T && item->HitTest( arect, false, 0 ) )
2912 return true;
2913 }
2914
2915 // Groups are not hit-tested; only their members
2916
2917 // No items were hit
2918 return false;
2919 }
2920}
2921
2922
2923bool FOOTPRINT::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
2924{
2925 using std::ranges::all_of;
2926 using std::ranges::any_of;
2927
2928 // If there are no pads, zones, or drawings, test footprint text instead.
2929 if( m_pads.empty() && m_zones.empty() && m_drawings.empty() )
2930 return KIGEOM::BoxHitTest( aPoly, GetBoundingBox( true ), aContained );
2931
2932 auto hitTest =
2933 [&]( const auto* aItem )
2934 {
2935 return aItem && aItem->HitTest( aPoly, aContained );
2936 };
2937
2938 // Filter out text items from the drawings, since they are selectable on their own,
2939 // and we don't want to select the whole footprint when text is hit. TextBox items are NOT
2940 // selectable on their own, so they are not excluded here.
2941 auto drawings = m_drawings | std::views::filter( []( const auto* aItem )
2942 {
2943 return aItem && aItem->Type() != PCB_TEXT_T;
2944 } );
2945
2946 // Test pads, zones and drawings with text excluded. PCB fields are also selectable
2947 // on their own, so they don't get tested. Groups are not hit-tested, only their members.
2948 // Bitmaps aren't selectable since they aren't displayed.
2949 if( aContained )
2950 {
2951 // All items must be contained in the selection poly.
2952 return all_of( drawings, hitTest )
2953 && all_of( m_pads, hitTest )
2954 && all_of( m_zones, hitTest );
2955 }
2956 else
2957 {
2958 // Any item intersecting the selection poly is sufficient.
2959 return any_of( drawings, hitTest )
2960 || any_of( m_pads, hitTest )
2961 || any_of( m_zones, hitTest );
2962 }
2963}
2964
2965
2966PAD* FOOTPRINT::FindPadByNumber( const wxString& aPadNumber, PAD* aSearchAfterMe ) const
2967{
2968 bool can_select = aSearchAfterMe ? false : true;
2969
2970 for( PAD* pad : m_pads )
2971 {
2972 if( !can_select && pad == aSearchAfterMe )
2973 {
2974 can_select = true;
2975 continue;
2976 }
2977
2978 if( can_select && pad->GetNumber() == aPadNumber )
2979 return pad;
2980 }
2981
2982 return nullptr;
2983}
2984
2985
2986PAD* FOOTPRINT::FindPadByUuid( const KIID& aUuid ) const
2987{
2988 for( PAD* pad : m_pads )
2989 {
2990 if( pad->m_Uuid == aUuid )
2991 return pad;
2992 }
2993
2994 return nullptr;
2995}
2996
2997
2998PAD* FOOTPRINT::GetPad( const VECTOR2I& aPosition, const LSET& aLayerMask )
2999{
3000 for( PAD* pad : m_pads )
3001 {
3002 // ... and on the correct layer.
3003 if( !( pad->GetLayerSet() & aLayerMask ).any() )
3004 continue;
3005
3006 if( pad->HitTest( aPosition ) )
3007 return pad;
3008 }
3009
3010 return nullptr;
3011}
3012
3013
3014std::vector<const PAD*> FOOTPRINT::GetPads( const wxString& aPadNumber, const PAD* aIgnore ) const
3015{
3016 std::vector<const PAD*> retv;
3017
3018 for( const PAD* pad : m_pads )
3019 {
3020 if( ( aIgnore && aIgnore == pad ) || ( pad->GetNumber() != aPadNumber ) )
3021 continue;
3022
3023 retv.push_back( pad );
3024 }
3025
3026 return retv;
3027}
3028
3029
3031{
3032 return m_pads.size();
3033}
3034
3035
3036std::set<wxString> FOOTPRINT::GetUniquePadNumbers() const
3037{
3038 std::set<wxString> usedNumbers;
3039
3040 // Create a set of used pad numbers
3041 for( PAD* pad : m_pads )
3042 {
3043 // Skip pads not on copper layers (used to build complex
3044 // solder paste shapes for instance)
3045 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
3046 continue;
3047
3048 // Skip pads with no name, because they are usually "mechanical"
3049 // pads, not "electrical" pads
3050 if( pad->GetNumber().IsEmpty() )
3051 continue;
3052
3053 usedNumbers.insert( pad->GetNumber() );
3054 }
3055
3056 return usedNumbers;
3057}
3058
3059
3061{
3062 // A pad number is "electrical" (i.e. maps to a schematic pin) when it is either:
3063 //
3064 // - purely numeric: "1", "42"
3065 //
3066 // - BGA / alphanumeric style: up to two leading letters followed by digits, e.g.
3067 // "A1", "B12", "AA3", "AB10"
3068 //
3069 // - ganged alphanumeric: two strings matching the above alphanumeric style
3070 // separated by an underscore, as used on the outside
3071 // ganged pins of a USB-C connector, e.g.
3072 // "A1_B12", "A12_B1"
3073 // these pads count as two each, as they will match
3074 // two schematic pins
3075 //
3076 // Mounting-pad designators such as "MP" do not end in a digit typically and are
3077 // intentionally excluded.
3078 auto isElectricalPadNumber =
3079 []( const wxString& num ) -> bool
3080 {
3081 if( num.IsEmpty() )
3082 return false;
3083
3084 // Walk past an optional alphabetic prefix of at most two characters.
3085 size_t i = 0;
3086 while( i < num.size() && wxIsalpha( num[i] ) )
3087 ++i;
3088
3089 // Prefix must be 0–2 letters; anything longer is not a pin number.
3090 if( i > 2 )
3091 return false;
3092
3093 // The remainder must be non-empty and consist entirely of digits.
3094 if( i == num.size() )
3095 return false; // no digits at all (e.g. "MP", "GND")
3096
3097 for( size_t j = i; j < num.size(); ++j )
3098 {
3099 if( !wxIsdigit( num[j] ) )
3100 return false;
3101 }
3102
3103 return true;
3104 };
3105
3106 std::set<wxString> counted;
3107
3108 for( const PAD* pad : m_pads )
3109 {
3110 // Must be on at least one copper layer.
3111 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
3112 continue;
3113
3114 // Skip NPTH (mechanical holes).
3115 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
3116 continue;
3117
3118 const wxString& num = pad->GetNumber();
3119
3120 if( isElectricalPadNumber( num ) )
3121 {
3122 counted.insert( num );
3123 }
3124 else if( num.Contains( '_' ) )
3125 {
3126 wxString first, second;
3127 first = num.BeforeFirst( '_', &second );
3128
3129 if( isElectricalPadNumber( first ) && isElectricalPadNumber( second ) )
3130 {
3131 counted.insert( first );
3132 counted.insert( second );
3133 }
3134 }
3135 }
3136
3137 return static_cast<unsigned>( counted.size() );
3138}
3139
3140
3142{
3143 if( nullptr == a3DModel )
3144 return;
3145
3146 if( !a3DModel->m_Filename.empty() )
3147 m_3D_Drawings.push_back( *a3DModel );
3148}
3149
3150
3152{
3153 if( PCB_3D_BODY* body = GetBody() )
3154 return *body;
3155
3156 PCB_3D_BODY* body = new PCB_3D_BODY( this );
3157 Add( body, ADD_MODE::APPEND );
3158 return *body;
3159}
3160
3161
3163{
3164 for( PCB_3D_BODY* body : m_bodies )
3165 Remove( body );
3166}
3167
3168bool FOOTPRINT::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
3169{
3170 if( aSearchData.searchMetadata )
3171 {
3172 if( EDA_ITEM::Matches( GetFPIDAsString(), aSearchData ) )
3173 return true;
3174
3175 if( EDA_ITEM::Matches( GetLibDescription(), aSearchData ) )
3176 return true;
3177
3178 if( EDA_ITEM::Matches( GetKeywords(), aSearchData ) )
3179 return true;
3180 }
3181
3182 return false;
3183}
3184
3185
3186// see footprint.h
3187INSPECT_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData,
3188 const std::vector<KICAD_T>& aScanTypes )
3189{
3190#if 0 && defined(DEBUG)
3191 std::cout << GetClass().mb_str() << ' ';
3192#endif
3193
3194 bool drawingsScanned = false;
3195
3196 for( KICAD_T scanType : aScanTypes )
3197 {
3198 switch( scanType )
3199 {
3200 case PCB_FOOTPRINT_T:
3201 if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
3202 return INSPECT_RESULT::QUIT;
3203
3204 break;
3205
3206 case PCB_PAD_T:
3207 if( IterateForward<PAD*>( m_pads, inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
3208 return INSPECT_RESULT::QUIT;
3209
3210 break;
3211
3212 case PCB_ZONE_T:
3213 if( IterateForward<ZONE*>( m_zones, inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
3214 return INSPECT_RESULT::QUIT;
3215
3216 break;
3217
3218 case PCB_FIELD_T:
3219 if( IterateForward<PCB_FIELD*>( m_fields, inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
3220 return INSPECT_RESULT::QUIT;
3221
3222 break;
3223
3224 case PCB_TEXT_T:
3225 case PCB_DIM_ALIGNED_T:
3226 case PCB_DIM_LEADER_T:
3227 case PCB_DIM_CENTER_T:
3228 case PCB_DIM_RADIAL_T:
3230 case PCB_SHAPE_T:
3231 case PCB_BARCODE_T:
3232 case PCB_TEXTBOX_T:
3233 case PCB_TABLE_T:
3234 case PCB_TABLECELL_T:
3235 if( !drawingsScanned )
3236 {
3237 if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, aScanTypes ) == INSPECT_RESULT::QUIT )
3238 return INSPECT_RESULT::QUIT;
3239
3240 drawingsScanned = true;
3241 }
3242
3243 break;
3244
3245 case PCB_GROUP_T:
3246 if( IterateForward<PCB_GROUP*>( m_groups, inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
3247 return INSPECT_RESULT::QUIT;
3248
3249 break;
3250
3251 case PCB_CONSTRAINT_T:
3252 if( IterateForward<PCB_CONSTRAINT*>( m_constraints, inspector, testData, { scanType } )
3254 {
3255 return INSPECT_RESULT::QUIT;
3256 }
3257
3258 break;
3259
3260 case PCB_POINT_T:
3261 if( IterateForward<PCB_POINT*>( m_points, inspector, testData, { scanType } ) == INSPECT_RESULT::QUIT )
3262 return INSPECT_RESULT::QUIT;
3263
3264 break;
3265
3266 case PCB_3D_BODY_T:
3267 if( IterateForward<PCB_3D_BODY*>( m_bodies, inspector, testData, { scanType } )
3269 {
3270 return INSPECT_RESULT::QUIT;
3271 }
3272
3273 break;
3274
3275 default:
3276 break;
3277 }
3278 }
3279
3281}
3282
3283
3284wxString FOOTPRINT::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
3285{
3286 wxString reference = GetReference();
3287
3288 if( reference.IsEmpty() )
3289 reference = _( "<no reference designator>" );
3290
3291 return wxString::Format( _( "Footprint %s" ), reference );
3292}
3293
3294
3295wxString FOOTPRINT::DisambiguateItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
3296{
3297 return wxString::Format( wxT( "%s (%s)" ),
3298 GetItemDescription( aUnitsProvider, aFull ),
3299 GetFPIDAsString() );
3300}
3301
3302
3304{
3305 return BITMAPS::module;
3306}
3307
3308
3310{
3311 return new FOOTPRINT( *this );
3312}
3313
3314
3315void FOOTPRINT::RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFunction, RECURSE_MODE aMode ) const
3316{
3317 try
3318 {
3319 for( PCB_FIELD* field : m_fields )
3320 aFunction( field );
3321
3322 for( PAD* pad : m_pads )
3323 aFunction( pad );
3324
3325 for( ZONE* zone : m_zones )
3326 aFunction( zone );
3327
3328 for( PCB_GROUP* group : m_groups )
3329 aFunction( group );
3330
3331 for( PCB_3D_BODY* body : m_bodies )
3332 aFunction( body );
3333
3334 for( PCB_CONSTRAINT* constraint : m_constraints )
3335 aFunction( constraint );
3336
3337 for( PCB_POINT* point : m_points )
3338 aFunction( point );
3339
3340 for( BOARD_ITEM* drawing : m_drawings )
3341 {
3342 aFunction( drawing );
3343
3344 if( aMode == RECURSE_MODE::RECURSE )
3345 drawing->RunOnChildren( aFunction, RECURSE_MODE::RECURSE );
3346 }
3347 }
3348 catch( std::bad_function_call& )
3349 {
3350 wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnChildren" ) );
3351 }
3352}
3353
3354
3355std::vector<int> FOOTPRINT::ViewGetLayers() const
3356{
3357 std::vector<int> layers;
3358
3359 layers.reserve( 6 );
3360 layers.push_back( LAYER_ANCHOR );
3361
3362 switch( m_layer )
3363 {
3364 default:
3365 wxASSERT_MSG( false, wxT( "Illegal layer" ) ); // do you really have footprints placed
3366 // on other layers?
3368
3369 case F_Cu:
3370 layers.push_back( LAYER_FOOTPRINTS_FR );
3371 break;
3372
3373 case B_Cu:
3374 layers.push_back( LAYER_FOOTPRINTS_BK );
3375 break;
3376 }
3377
3378 layers.push_back( LAYER_CONFLICTS_SHADOW );
3379
3380 // If there are no pads, and only drawings on a silkscreen layer, then report the silkscreen
3381 // layer as well so that the component can be edited with the silkscreen layer
3382 bool f_silk = false, b_silk = false, non_silk = false;
3383
3384 for( BOARD_ITEM* item : m_drawings )
3385 {
3386 if( item->GetLayer() == F_SilkS )
3387 f_silk = true;
3388 else if( item->GetLayer() == B_SilkS )
3389 b_silk = true;
3390 else
3391 non_silk = true;
3392 }
3393
3394 if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
3395 {
3396 if( f_silk )
3397 layers.push_back( F_SilkS );
3398
3399 if( b_silk )
3400 layers.push_back( B_SilkS );
3401 }
3402
3403 return layers;
3404}
3405
3406
3407double FOOTPRINT::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
3408{
3409 if( aLayer == LAYER_CONFLICTS_SHADOW )
3410 {
3411 // The painter draws nothing here for a footprint that is not in conflict
3412 if( !IsConflicting() )
3413 return LOD_HIDE;
3414
3415 // The locked shadow shape is shown only if the footprint itself is visible
3416 if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
3417 return LOD_SHOW;
3418
3419 if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
3420 return LOD_SHOW;
3421
3422 return LOD_HIDE;
3423 }
3424
3425 // Only show anchors if the layer the footprint is on is visible
3426 if( aLayer == LAYER_ANCHOR && !aView->IsLayerVisible( m_layer ) )
3427 return LOD_HIDE;
3428
3429 int layer = ( m_layer == F_Cu ) ? LAYER_FOOTPRINTS_FR :
3431
3432 // Currently this is only pertinent for the anchor layer; everything else is drawn from the
3433 // children.
3434 // The "good" value is experimentally chosen.
3435 constexpr double MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY = 1.5;
3436
3437 if( aView->IsLayerVisible( layer ) )
3438 return MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY;
3439
3440 return LOD_HIDE;
3441}
3442
3443
3445{
3446 BOX2I area = GetBoundingBox( true );
3447
3448 // Inflate in case clearance lines are drawn around pads, etc.
3449 if( const BOARD* board = GetBoard() )
3450 {
3451 int biggest_clearance = board->GetMaxClearanceValue();
3452 area.Inflate( biggest_clearance );
3453 }
3454
3455 return area;
3456}
3457
3458
3459bool FOOTPRINT::IsLibNameValid( const wxString & aName )
3460{
3461 const wxChar * invalids = StringLibNameInvalidChars( false );
3462
3463 if( aName.find_first_of( invalids ) != std::string::npos )
3464 return false;
3465
3466 return true;
3467}
3468
3469
3470const wxChar* FOOTPRINT::StringLibNameInvalidChars( bool aUserReadable )
3471{
3472 // Filename rules are a superset of LIB_ID rules; the machine-readable list is the shared
3473 // source of truth, while the human-readable spelling stays local.
3474 static const wxString invalidChars = GetLibFilenameForbiddenChars();
3475 static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
3476
3477 if( aUserReadable )
3478 return invalidCharsReadable;
3479 else
3480 return invalidChars.wc_str();
3481}
3482
3483
3484void FOOTPRINT::Move( const VECTOR2I& aMoveVector )
3485{
3486 if( aMoveVector.x == 0 && aMoveVector.y == 0 )
3487 return;
3488
3489 VECTOR2I newpos = m_transform.GetTranslate() + aMoveVector;
3490 SetPosition( newpos );
3491}
3492
3493
3494void FOOTPRINT::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
3495{
3496 if( aAngle == ANGLE_0 )
3497 return;
3498
3499 EDA_ANGLE orientation = GetOrientation();
3500 EDA_ANGLE newOrientation = orientation + aAngle;
3501 VECTOR2I newpos = m_transform.GetTranslate();
3502 RotatePoint( newpos, aRotCentre, aAngle );
3503 SetPosition( newpos );
3504 SetOrientation( newOrientation );
3505
3506 for( PCB_FIELD* field : m_fields )
3507 field->KeepUpright();
3508
3509 for( BOARD_ITEM* item : m_drawings )
3510 {
3511 if( item->Type() == PCB_TEXT_T )
3512 static_cast<PCB_TEXT*>( item )->KeepUpright();
3513 }
3514}
3515
3516
3518{
3519 wxASSERT( aLayer == F_Cu || aLayer == B_Cu );
3520
3521 if( aLayer != GetLayer() )
3523}
3524
3525
3526void FOOTPRINT::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
3527{
3528 // Move footprint to its final position:
3529 VECTOR2I finalPos = m_transform.GetTranslate();
3530
3531 // Now Flip the footprint.
3532 // Flipping a footprint is a specific transform: it is not mirrored like a text.
3533 // We have to change the side, and ensure the footprint rotation is modified according to the
3534 // transform, because this parameter is used in pick and place files, and when updating the
3535 // footprint from library.
3536 // When flipped around the X axis (Y coordinates changed) orientation is negated
3537 // When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
3538 // Because it is specific to a footprint, we flip around the X axis, and after rotate 180 deg
3539
3540 MIRROR( finalPos.y, aCentre.y );
3541
3542 SetPosition( finalPos );
3543
3544 // Flip layer
3546
3547 const VECTOR2I pos = m_transform.GetTranslate();
3548
3549 // Children mirror their lib-frame state directly so the result does not
3550 // depend on the parent rotation at the time of the call. The parent
3551 // rotation is negated once at the end.
3552 for( PCB_FIELD* field : m_fields )
3553 field->Flip( pos, FLIP_DIRECTION::TOP_BOTTOM );
3554
3555 for( PAD* pad : m_pads )
3556 pad->Flip( pos, FLIP_DIRECTION::TOP_BOTTOM );
3557
3558 for( ZONE* zone : m_zones )
3559 zone->Flip( pos, FLIP_DIRECTION::TOP_BOTTOM );
3560
3561 for( BOARD_ITEM* item : m_drawings )
3562 item->Flip( pos, FLIP_DIRECTION::TOP_BOTTOM );
3563
3564 // Points move but don't flip layer
3565 for( PCB_POINT* point : m_points )
3566 point->Flip( pos, FLIP_DIRECTION::TOP_BOTTOM );
3567
3568 EDA_ANGLE newOrientation = -m_transform.GetRotate();
3569 newOrientation.Normalize180();
3570 m_transform.SetRotate( newOrientation );
3571
3572 // Refresh derived caches now that the final rotation is in place.
3573 for( PCB_FIELD* field : m_fields )
3574 field->OnFootprintTransformed();
3575
3576 for( PAD* pad : m_pads )
3577 pad->OnFootprintTransformed();
3578
3579 for( ZONE* zone : m_zones )
3580 zone->OnFootprintTransformed();
3581
3582 for( BOARD_ITEM* item : m_drawings )
3583 {
3584 if( item->Type() == PCB_TEXT_T || item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXTBOX_T
3585 || item->Type() == PCB_BARCODE_T || item->Type() == PCB_TABLE_T
3586 || BaseType( item->Type() ) == PCB_DIMENSION_T )
3587 {
3588 item->OnFootprintTransformed();
3589 }
3590 }
3591
3592 for( PCB_POINT* point : m_points )
3593 point->OnFootprintTransformed();
3594
3595 // Swap the courtyard sides, then mirror in the same way as everything else.
3596 if( m_courtyard_cache )
3597 {
3598 std::swap( m_courtyard_cache->back, m_courtyard_cache->front );
3599 m_courtyard_cache->back.Mirror( pos, FLIP_DIRECTION::TOP_BOTTOM );
3600 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3601
3602 m_courtyard_cache->front.Mirror( pos, FLIP_DIRECTION::TOP_BOTTOM );
3603 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3604 }
3605
3606 for( PCB_3D_BODY* body : m_bodies )
3607 body->Flip( pos, aFlipDirection );
3608
3609 if( m_geometry_cache )
3610 m_geometry_cache->hull.Mirror( pos, FLIP_DIRECTION::TOP_BOTTOM );
3611
3612 // Now rotate 180 deg if required
3613 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
3614 Rotate( aCentre, ANGLE_180 );
3615
3616 if( m_geometry_cache )
3617 m_geometry_cache->text_excluded_bbox_timestamp = 0;
3618
3619 m_flipped = ( GetLayer() == B_Cu );
3620}
3621
3622
3624{
3625 BOARD_ITEM::SetLayer( aLayer );
3626 m_flipped = ( aLayer == B_Cu );
3627}
3628
3629
3630void FOOTPRINT::SetTransformScale( double aScaleX, double aScaleY )
3631{
3632 // Reject zero, negative, and non-finite scales: they produce a degenerate transform.
3633 if( !std::isfinite( aScaleX ) || !std::isfinite( aScaleY ) || aScaleX <= 0.0 || aScaleY <= 0.0 )
3634 return;
3635
3636 const double oldSx = m_transform.GetScaleX();
3637 const double oldSy = m_transform.GetScaleY();
3638 const double ratioX = aScaleX / oldSx;
3639 const double ratioY = aScaleY / oldSy;
3640 const double linearFactor = ( aScaleX + aScaleY ) / ( oldSx + oldSy );
3641
3642 m_transform.SetScale( aScaleX, aScaleY );
3643
3644 const VECTOR2I anchor = m_transform.GetTranslate();
3645 const EDA_ANGLE parentRotate = m_transform.GetRotate();
3646
3647 for( PAD* pad : m_pads )
3648 pad->OnFootprintRescaled( ratioX, ratioY, linearFactor, anchor, parentRotate );
3649
3650 for( PCB_FIELD* field : m_fields )
3651 field->OnFootprintRescaled( ratioX, ratioY, linearFactor, anchor, parentRotate );
3652
3653 for( BOARD_ITEM* item : m_drawings )
3654 item->OnFootprintRescaled( ratioX, ratioY, linearFactor, anchor, parentRotate );
3655
3656 for( ZONE* zone : m_zones )
3657 zone->OnFootprintRescaled( ratioX, ratioY, linearFactor, anchor, parentRotate );
3658
3659 for( PCB_POINT* point : m_points )
3660 point->OnFootprintTransformed();
3661
3662 if( m_geometry_cache )
3663 {
3664 m_geometry_cache->bounding_box_timestamp = 0;
3665 m_geometry_cache->text_excluded_bbox_timestamp = 0;
3666 }
3667
3668 m_courtyard_cache.reset();
3669}
3670
3671
3672void FOOTPRINT::RescaleAroundPoint( const VECTOR2I& aCenter, double aSx, double aSy )
3673{
3674 TRANSFORM_TRS rescaled = m_transform.RescaleAround( aCenter, aSx, aSy );
3675
3676 SetPosition( rescaled.GetTranslate() );
3677 SetTransformScale( rescaled.GetScaleX(), rescaled.GetScaleY() );
3678}
3679
3680
3682{
3683 VECTOR2I delta = aPos - m_transform.GetTranslate();
3684
3685 m_transform.SetTranslate( aPos );
3686
3687 for( PCB_FIELD* field : m_fields )
3688 field->OnFootprintTransformed();
3689
3690 for( PAD* pad : m_pads )
3691 pad->OnFootprintTransformed();
3692
3693 for( ZONE* zone : m_zones )
3694 zone->OnFootprintTransformed();
3695
3696 for( PCB_POINT* point : m_points )
3697 point->OnFootprintTransformed();
3698
3699 for( BOARD_ITEM* item : m_drawings )
3700 {
3701 if( item->Type() == PCB_TEXT_T || item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXTBOX_T
3702 || item->Type() == PCB_BARCODE_T || item->Type() == PCB_TABLE_T
3703 || BaseType( item->Type() ) == PCB_DIMENSION_T )
3704 {
3705 item->OnFootprintTransformed();
3706 }
3707 else
3708 {
3709 item->Move( delta );
3710 }
3711 }
3712
3713 if( m_geometry_cache )
3714 {
3715 m_geometry_cache->bounding_box.Move( delta );
3716 m_geometry_cache->text_excluded_bbox.Move( delta );
3717 m_geometry_cache->hull.Move( delta );
3718 }
3719
3720 // The geometry work has been conserved by using Move(). But the hashes
3721 // need to be updated, otherwise the cached polygons will still be rebuild.
3722 if( m_courtyard_cache )
3723 {
3724 m_courtyard_cache->back.Move( delta );
3725 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3726 m_courtyard_cache->front.Move( delta );
3727 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3728 }
3729}
3730
3731
3732void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
3733{
3734 /*
3735 * Move the reference point of the footprint
3736 * the footprints elements (pads, outlines, edges .. ) are moved
3737 * but:
3738 * - the footprint position is not modified.
3739 * - the relative (local) coordinates of these items are modified
3740 * - Draw coordinates are updated
3741 */
3742
3743 // Update (move) the relative coordinates relative to the new anchor point.
3744 VECTOR2I moveVector = aMoveVector;
3745 RotatePoint( moveVector, -GetOrientation() );
3746
3747 // Update field local coordinates
3748 for( PCB_FIELD* field : m_fields )
3749 field->Move( moveVector );
3750
3751 // Update the pad local coordinates.
3752 for( PAD* pad : m_pads )
3753 pad->Move( moveVector );
3754
3755 // Update the draw element coordinates.
3756 for( BOARD_ITEM* item : GraphicalItems() )
3757 item->Move( moveVector );
3758
3759 // Update the keepout zones
3760 for( ZONE* zone : Zones() )
3761 zone->Move( moveVector );
3762
3763 // Update the point local coordinates.
3764 for( PCB_POINT* point : m_points )
3765 point->Move( moveVector );
3766
3767 // Update the 3D models
3768 for( FP_3DMODEL& model : Models() )
3769 {
3770 model.m_Offset.x += pcbIUScale.IUTomm( moveVector.x );
3771 model.m_Offset.y -= pcbIUScale.IUTomm( moveVector.y );
3772 }
3773
3774 if( m_geometry_cache )
3775 {
3776 m_geometry_cache->bounding_box.Move( moveVector );
3777 m_geometry_cache->text_excluded_bbox.Move( moveVector );
3778 m_geometry_cache->hull.Move( moveVector );
3779 }
3780
3781 // The geometry work have been conserved by using Move(). But the hashes
3782 // need to be updated, otherwise the cached polygons will still be rebuild.
3783 if( m_courtyard_cache )
3784 {
3785 m_courtyard_cache->back.Move( moveVector );
3786 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3787 m_courtyard_cache->front.Move( moveVector );
3788 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3789 }
3790}
3791
3792
3793void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
3794{
3795 EDA_ANGLE angleChange = aNewAngle - m_transform.GetRotate(); // change in rotation
3796
3797 EDA_ANGLE newAngle = aNewAngle;
3798 newAngle.Normalize180();
3799 m_transform.SetRotate( newAngle );
3800
3801 const VECTOR2I rotationCenter = GetPosition();
3802
3803 for( PCB_FIELD* field : m_fields )
3804 field->OnFootprintTransformed();
3805
3806 for( PAD* pad : m_pads )
3807 pad->OnFootprintTransformed();
3808
3809 for( ZONE* zone : m_zones )
3810 zone->OnFootprintTransformed();
3811
3812 for( PCB_POINT* point : m_points )
3813 point->OnFootprintTransformed();
3814
3815 for( BOARD_ITEM* item : m_drawings )
3816 {
3817 if( item->Type() == PCB_TEXT_T || item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXTBOX_T
3818 || item->Type() == PCB_BARCODE_T || item->Type() == PCB_TABLE_T
3819 || BaseType( item->Type() ) == PCB_DIMENSION_T )
3820 {
3821 item->OnFootprintTransformed();
3822 }
3823 else
3824 {
3825 item->Rotate( rotationCenter, angleChange );
3826 }
3827 }
3828
3829 if( m_geometry_cache )
3830 m_geometry_cache->text_excluded_bbox_timestamp = 0;
3831
3832 if( m_courtyard_cache )
3833 {
3834 m_courtyard_cache->front.Rotate( angleChange, rotationCenter );
3835 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3836
3837 m_courtyard_cache->back.Rotate( angleChange, rotationCenter );
3838 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3839 }
3840
3841 if( m_geometry_cache )
3842 m_geometry_cache->hull.Rotate( angleChange, rotationCenter );
3843}
3844
3845
3846BOARD_ITEM* FOOTPRINT::Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit ) const
3847{
3848 FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate( addToParentGroup, aCommit ) );
3849
3850 // Clones keep child UUIDs so cloned constraints still resolve to them
3851 // Map old ids to new before reset else constraints below strand
3852 std::map<KIID, KIID> idMap;
3853
3854 dupe->RunOnChildren(
3855 [&]( BOARD_ITEM* child )
3856 {
3857 KIID oldId = child->m_Uuid;
3858 child->ResetUuidDirect();
3859 idMap[oldId] = child->m_Uuid;
3860 },
3862
3863 for( PCB_CONSTRAINT* constraint : dupe->Constraints() )
3864 constraint->RemapKIIDs( idMap );
3865
3866 return dupe;
3867}
3868
3869
3870BOARD_ITEM* FOOTPRINT::DuplicateItem( bool addToParentGroup, BOARD_COMMIT* aCommit,
3871 const BOARD_ITEM* aItem, bool addToFootprint )
3872{
3873 BOARD_ITEM* new_item = nullptr;
3874
3875 switch( aItem->Type() )
3876 {
3877 case PCB_PAD_T:
3878 {
3879 PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
3880 new_pad->ResetUuidDirect();
3881
3882 if( addToFootprint )
3883 m_pads.push_back( new_pad );
3884
3885 new_item = new_pad;
3886 break;
3887 }
3888
3889 case PCB_ZONE_T:
3890 {
3891 ZONE* new_zone = new ZONE( *static_cast<const ZONE*>( aItem ) );
3892 new_zone->ResetUuidDirect();
3893
3894 if( addToFootprint )
3895 m_zones.push_back( new_zone );
3896
3897 new_item = new_zone;
3898 break;
3899 }
3900
3901 case PCB_POINT_T:
3902 {
3903 PCB_POINT* new_point = new PCB_POINT( *static_cast<const PCB_POINT*>( aItem ) );
3904 new_point->ResetUuidDirect();
3905
3906 if( addToFootprint )
3907 m_points.push_back( new_point );
3908
3909 new_item = new_point;
3910 break;
3911 }
3912
3913 case PCB_FIELD_T:
3914 {
3915 PCB_FIELD* new_field = new PCB_FIELD( *static_cast<const PCB_FIELD*>( aItem ) );
3916 new_field->ResetUuidDirect();
3917
3918 switch( static_cast<const PCB_FIELD*>( aItem )->GetId() )
3919 {
3920 case FIELD_T::REFERENCE: new_field->SetText( wxT( "${REFERENCE}" ) ); break;
3921 case FIELD_T::VALUE: new_field->SetText( wxT( "${VALUE}" ) ); break;
3922 case FIELD_T::DATASHEET: new_field->SetText( wxT( "${DATASHEET}" ) ); break;
3923 default: break;
3924 }
3925
3926 if( addToFootprint )
3927 Add( new_field );
3928
3929 new_item = new_field;
3930 break;
3931 }
3932
3933 case PCB_TEXT_T:
3934 {
3935 PCB_TEXT* new_text = new PCB_TEXT( *static_cast<const PCB_TEXT*>( aItem ) );
3936 new_text->ResetUuidDirect();
3937
3938 if( addToFootprint )
3939 Add( new_text );
3940
3941 new_item = new_text;
3942 break;
3943 }
3944
3945 case PCB_SHAPE_T:
3946 {
3947 PCB_SHAPE* new_shape = new PCB_SHAPE( *static_cast<const PCB_SHAPE*>( aItem ) );
3948 new_shape->ResetUuidDirect();
3949
3950 if( addToFootprint )
3951 Add( new_shape );
3952
3953 new_item = new_shape;
3954 break;
3955 }
3956
3957 case PCB_BARCODE_T:
3958 {
3959 PCB_BARCODE* new_barcode = new PCB_BARCODE( *static_cast<const PCB_BARCODE*>( aItem ) );
3960 new_barcode->ResetUuidDirect();
3961
3962 if( addToFootprint )
3963 Add( new_barcode );
3964
3965 new_item = new_barcode;
3966 break;
3967 }
3968
3970 {
3971 PCB_REFERENCE_IMAGE* new_image = new PCB_REFERENCE_IMAGE( *static_cast<const PCB_REFERENCE_IMAGE*>( aItem ) );
3972 new_image->ResetUuidDirect();
3973
3974 if( addToFootprint )
3975 Add( new_image );
3976
3977 new_item = new_image;
3978 break;
3979 }
3980
3981 case PCB_TEXTBOX_T:
3982 {
3983 PCB_TEXTBOX* new_textbox = new PCB_TEXTBOX( *static_cast<const PCB_TEXTBOX*>( aItem ) );
3984 new_textbox->ResetUuidDirect();
3985
3986 if( addToFootprint )
3987 Add( new_textbox );
3988
3989 new_item = new_textbox;
3990 break;
3991 }
3992
3993 case PCB_DIM_ALIGNED_T:
3994 case PCB_DIM_LEADER_T:
3995 case PCB_DIM_CENTER_T:
3996 case PCB_DIM_RADIAL_T:
3998 {
3999 PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate( addToParentGroup,
4000 aCommit ) );
4001
4002 if( addToFootprint )
4003 Add( dimension );
4004
4005 new_item = dimension;
4006 break;
4007 }
4008
4009 case PCB_TABLE_T:
4010 {
4011 new_item = aItem->Duplicate( addToParentGroup, aCommit );
4012
4013 if( addToFootprint )
4014 Add( new_item );
4015
4016 break;
4017 }
4018
4019 case PCB_GROUP_T:
4020 {
4021 PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate( addToParentGroup, aCommit );
4022
4023 if( addToFootprint )
4024 {
4025 group->RunOnChildren(
4026 [&]( BOARD_ITEM* aCurrItem )
4027 {
4028 Add( aCurrItem );
4029 },
4031
4032 Add( group );
4033 }
4034
4035 new_item = group;
4036 break;
4037 }
4038
4039 case PCB_FOOTPRINT_T:
4040 // Ignore the footprint itself
4041 break;
4042
4043 default:
4044 // Un-handled item for duplication
4045 wxFAIL_MSG( wxT( "Duplication not supported for items of class " ) + aItem->GetClass() );
4046 break;
4047 }
4048
4049 return new_item;
4050}
4051
4052
4053wxString FOOTPRINT::GetNextPadNumber( const wxString& aLastPadNumber ) const
4054{
4055 std::set<wxString> usedNumbers;
4056
4057 // Create a set of used pad numbers
4058 for( PAD* pad : m_pads )
4059 usedNumbers.insert( pad->GetNumber() );
4060
4061 // Pad numbers aren't technically reference designators, but the formatting is close enough
4062 // for these to give us what we need.
4063 wxString prefix = UTIL::GetRefDesPrefix( aLastPadNumber );
4064 int num = GetTrailingInt( aLastPadNumber );
4065
4066 while( usedNumbers.count( wxString::Format( wxT( "%s%d" ), prefix, num ) ) )
4067 num++;
4068
4069 return wxString::Format( wxT( "%s%d" ), prefix, num );
4070}
4071
4072
4074{
4075 // Auto-position reference and value
4076 BOX2I bbox = GetBoundingBox( false );
4077 bbox.Inflate( pcbIUScale.mmToIU( 0.2 ) ); // Gap between graphics and text
4078
4079 if( Reference().GetPosition() == VECTOR2I( 0, 0 ) )
4080 {
4084
4085 Reference().SetX( bbox.GetCenter().x );
4086 Reference().SetY( bbox.GetTop() - Reference().GetTextSize().y / 2 );
4087 }
4088
4089 if( Value().GetPosition() == VECTOR2I( 0, 0 ) )
4090 {
4094
4095 Value().SetX( bbox.GetCenter().x );
4096 Value().SetY( bbox.GetBottom() + Value().GetTextSize().y / 2 );
4097 }
4098}
4099
4100
4102{
4103 const wxString& refdes = GetReference();
4104
4105 SetReference( wxString::Format( wxT( "%s%i" ),
4106 UTIL::GetRefDesPrefix( refdes ),
4107 GetTrailingInt( refdes ) + aDelta ) );
4108}
4109
4110
4111double FOOTPRINT::GetCoverageArea( int aTextMargin ) const
4112{
4114 return polygonArea( hull );
4115}
4116
4117
4118double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
4119{
4120 int textMargin = aCollector.GetGuide()->Accuracy();
4121
4122 SHAPE_POLY_SET footprintRegion( GetBoundingHull() );
4123 SHAPE_POLY_SET coveredRegion;
4124
4126
4127 TransformFPShapesToPolySet( coveredRegion, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF,
4129 true, /* include text */
4130 false, /* include shapes */
4131 false /* include private items */ );
4132
4133 for( int i = 0; i < aCollector.GetCount(); ++i )
4134 {
4135 const BOARD_ITEM* item = aCollector[i];
4136
4137 switch( item->Type() )
4138 {
4139 case PCB_FIELD_T:
4140 case PCB_TEXT_T:
4141 case PCB_TEXTBOX_T:
4142 case PCB_SHAPE_T:
4143 case PCB_BARCODE_T:
4144 case PCB_TRACE_T:
4145 case PCB_ARC_T:
4146 case PCB_VIA_T:
4147 if( item->GetParent() != this )
4148 {
4149 item->TransformShapeToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
4150 ERROR_OUTSIDE );
4151 }
4152 break;
4153
4154 case PCB_FOOTPRINT_T:
4155 if( item != this )
4156 {
4157 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( item );
4158 coveredRegion.AddOutline( footprint->GetBoundingHull().Outline( 0 ) );
4159 }
4160 break;
4161
4162 default:
4163 break;
4164 }
4165 }
4166
4167 coveredRegion.BooleanIntersection( footprintRegion );
4168
4169 double footprintRegionArea = polygonArea( footprintRegion );
4170 double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion );
4171 double coveredArea = footprintRegionArea - uncoveredRegionArea;
4172
4173 // Avoid div-by-zero (this will result in the disambiguate dialog)
4174 if( footprintRegionArea == 0 )
4175 return 1.0;
4176
4177 double ratio = coveredArea / footprintRegionArea;
4178
4179 // Test for negative ratio (should not occur).
4180 // better to be conservative (this will result in the disambiguate dialog)
4181 if( ratio < 0.0 )
4182 return 1.0;
4183
4184 return std::min( ratio, 1.0 );
4185}
4186
4187
4188std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash,
4189 DRC_CONSTRAINT_T aUsage ) const
4190{
4191 std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
4192
4193 // There are several possible interpretations here:
4194 // 1) the bounding box (without or without invisible items)
4195 // 2) just the pads and "edges" (ie: non-text graphic items)
4196 // 3) the courtyard
4197
4198 // We'll go with (2) for now, unless the caller is clearly looking for (3)
4199
4200 if( aLayer == F_CrtYd || aLayer == B_CrtYd )
4201 {
4202 const SHAPE_POLY_SET& courtyard = GetCourtyard( aLayer );
4203
4204 if( courtyard.OutlineCount() == 0 ) // malformed/empty polygon
4205 return shape;
4206
4207 shape->AddShape( new SHAPE_SIMPLE( courtyard.COutline( 0 ) ) );
4208 }
4209 else
4210 {
4211 for( PAD* pad : Pads() )
4212 shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash, aUsage )->Clone() );
4213
4214 for( BOARD_ITEM* item : GraphicalItems() )
4215 {
4216 if( item->Type() == PCB_SHAPE_T )
4217 shape->AddShape( item->GetEffectiveShape( aLayer, aFlash, aUsage )->Clone() );
4218 else if( item->Type() == PCB_BARCODE_T )
4219 shape->AddShape( item->GetEffectiveShape( aLayer, aFlash, aUsage )->Clone() );
4220 }
4221 }
4222
4223 return shape;
4224}
4225
4226
4228{
4229 std::lock_guard<std::mutex> lock( m_courtyard_cache_mutex );
4230
4232 || m_courtyard_cache->front_hash != m_courtyard_cache->front.GetHash()
4233 || m_courtyard_cache->back_hash != m_courtyard_cache->back.GetHash() )
4234 {
4235 const_cast<FOOTPRINT*>(this)->BuildCourtyardCaches();
4236 }
4237
4238 return GetCachedCourtyard( aLayer );
4239}
4240
4241
4243{
4244 if( !m_courtyard_cache )
4245 m_courtyard_cache = std::make_unique<FOOTPRINT_COURTYARD_CACHE_DATA>();
4246
4247 if( IsBackLayer( aLayer ) )
4248 return m_courtyard_cache->back;
4249 else
4250 return m_courtyard_cache->front;
4251}
4252
4253
4255{
4256 if( !m_courtyard_cache )
4257 m_courtyard_cache = std::make_unique<FOOTPRINT_COURTYARD_CACHE_DATA>();
4258
4259 m_courtyard_cache->front.RemoveAllContours();
4260 m_courtyard_cache->back.RemoveAllContours();
4262
4263 // Build the courtyard area from graphic items on the courtyard.
4264 // Only PCB_SHAPE_T have meaning, graphic texts are ignored.
4265 // Collect items:
4266 std::vector<PCB_SHAPE*> list_front;
4267 std::vector<PCB_SHAPE*> list_back;
4268 std::map<int, int> front_width_histogram;
4269 std::map<int, int> back_width_histogram;
4270
4271 for( BOARD_ITEM* item : GraphicalItems() )
4272 {
4273 if( item->GetLayer() == B_CrtYd && item->Type() == PCB_SHAPE_T )
4274 {
4275 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
4276 list_back.push_back( shape );
4277 back_width_histogram[ shape->GetStroke().GetWidth() ]++;
4278 }
4279
4280 if( item->GetLayer() == F_CrtYd && item->Type() == PCB_SHAPE_T )
4281 {
4282 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
4283 list_front.push_back( shape );
4284 front_width_histogram[ shape->GetStroke().GetWidth() ]++;
4285 }
4286 }
4287
4288 if( !list_front.size() && !list_back.size() )
4289 return;
4290
4291 int maxError = pcbIUScale.mmToIU( 0.005 ); // max error for polygonization
4292 int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
4293
4294 if( ConvertOutlineToPolygon( list_front, m_courtyard_cache->front, maxError, chainingEpsilon,
4295 true, aErrorHandler ) )
4296 {
4297 int width = 0;
4298
4299 // Touching courtyards, or courtyards -at- the clearance distance are legal.
4300 // Use maxError here because that is the allowed deviation when transforming arcs/circles to
4301 // polygons.
4302 m_courtyard_cache->front.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
4303
4304 m_courtyard_cache->front.CacheTriangulation();
4305 auto max = std::max_element( front_width_histogram.begin(), front_width_histogram.end(),
4306 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
4307 {
4308 return a.second < b.second;
4309 } );
4310
4311 if( max != front_width_histogram.end() )
4312 width = max->first;
4313
4314 if( width == 0 )
4315 width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH );
4316
4317 if( m_courtyard_cache->front.OutlineCount() > 0 )
4318 m_courtyard_cache->front.Outline( 0 ).SetWidth( width );
4319 }
4320 else
4321 {
4323 }
4324
4325 if( ConvertOutlineToPolygon( list_back, m_courtyard_cache->back, maxError, chainingEpsilon, true,
4326 aErrorHandler ) )
4327 {
4328 int width = 0;
4329
4330 // Touching courtyards, or courtyards -at- the clearance distance are legal.
4331 m_courtyard_cache->back.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
4332
4333 m_courtyard_cache->back.CacheTriangulation();
4334 auto max = std::max_element( back_width_histogram.begin(), back_width_histogram.end(),
4335 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
4336 {
4337 return a.second < b.second;
4338 } );
4339
4340 if( max != back_width_histogram.end() )
4341 width = max->first;
4342
4343 if( width == 0 )
4344 width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH );
4345
4346 if( m_courtyard_cache->back.OutlineCount() > 0 )
4347 m_courtyard_cache->back.Outline( 0 ).SetWidth( width );
4348 }
4349 else
4350 {
4352 }
4353
4354 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
4355 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
4356}
4357
4358
4360{
4361 m_netTieCache.clear();
4362 std::map<wxString, int> map = MapPadNumbersToNetTieGroups();
4363 std::map<PCB_LAYER_ID, std::vector<PCB_SHAPE*>> layer_shapes;
4364 BOARD* board = GetBoard();
4365
4366 std::for_each( m_drawings.begin(), m_drawings.end(),
4367 [&]( BOARD_ITEM* item )
4368 {
4369 if( item->Type() != PCB_SHAPE_T )
4370 return;
4371
4372 for( PCB_LAYER_ID layer : item->GetLayerSet() )
4373 {
4374 if( !IsCopperLayer( layer ) )
4375 continue;
4376
4377 if( board && !board->GetEnabledLayers().Contains( layer ) )
4378 continue;
4379
4380 layer_shapes[layer].push_back( static_cast<PCB_SHAPE*>( item ) );
4381 }
4382 } );
4383
4384 for( size_t ii = 0; ii < m_pads.size(); ++ii )
4385 {
4386 PAD* pad = m_pads[ ii ];
4387 bool has_nettie = false;
4388
4389 auto it = map.find( pad->GetNumber() );
4390
4391 if( it == map.end() || it->second < 0 )
4392 continue;
4393
4394 for( size_t jj = 0; jj < m_pads.size(); ++jj )
4395 {
4396 if( jj == ii )
4397 continue;
4398
4399 PAD* other = m_pads[ jj ];
4400
4401 auto it2 = map.find( other->GetNumber() );
4402
4403 if( it2 == map.end() || it2->second < 0 )
4404 continue;
4405
4406 if( it2->second == it->second )
4407 {
4408 m_netTieCache[pad].insert( pad->GetNetCode() );
4409 m_netTieCache[pad].insert( other->GetNetCode() );
4410 m_netTieCache[other].insert( other->GetNetCode() );
4411 m_netTieCache[other].insert( pad->GetNetCode() );
4412 has_nettie = true;
4413 }
4414 }
4415
4416 if( !has_nettie )
4417 continue;
4418
4419 for( auto& [ layer, shapes ] : layer_shapes )
4420 {
4421 auto pad_shape = pad->GetEffectiveShape( layer );
4422
4423 for( auto other_shape : shapes )
4424 {
4425 auto shape = other_shape->GetEffectiveShape( layer );
4426
4427 if( pad_shape->Collide( shape.get() ) )
4428 {
4429 std::set<int>& nettie = m_netTieCache[pad];
4430 m_netTieCache[other_shape].insert( nettie.begin(), nettie.end() );
4431 }
4432 }
4433 }
4434 }
4435}
4436
4437
4438std::map<wxString, int> FOOTPRINT::MapPadNumbersToNetTieGroups() const
4439{
4440 std::map<wxString, int> padNumberToGroupIdxMap;
4441
4442 for( const PAD* pad : m_pads )
4443 padNumberToGroupIdxMap[ pad->GetNumber() ] = -1;
4444
4445 auto processPad =
4446 [&]( wxString aPad, int aGroup )
4447 {
4448 aPad.Trim( true ).Trim( false );
4449
4450 if( !aPad.IsEmpty() )
4451 padNumberToGroupIdxMap[ aPad ] = aGroup;
4452 };
4453
4454 for( int ii = 0; ii < (int) m_netTiePadGroups.size(); ++ii )
4455 {
4456 wxString group( m_netTiePadGroups[ ii ] );
4457 bool esc = false;
4458 wxString pad;
4459
4460 for( wxUniCharRef ch : group )
4461 {
4462 if( esc )
4463 {
4464 esc = false;
4465 pad.Append( ch );
4466 continue;
4467 }
4468
4469 switch( static_cast<unsigned char>( ch ) )
4470 {
4471 case '\\':
4472 esc = true;
4473 break;
4474
4475 case ',':
4476 processPad( pad, ii );
4477 pad.Clear();
4478 break;
4479
4480 default:
4481 pad.Append( ch );
4482 break;
4483 }
4484 }
4485
4486 processPad( pad, ii );
4487 }
4488
4489 return padNumberToGroupIdxMap;
4490}
4491
4492
4493std::vector<PAD*> FOOTPRINT::GetNetTiePads( PAD* aPad ) const
4494{
4495 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
4496 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
4497
4498 std::map<wxString, int> padToNetTieGroupMap = MapPadNumbersToNetTieGroups();
4499 int groupIdx = padToNetTieGroupMap[ aPad->GetNumber() ];
4500 std::vector<PAD*> otherPads;
4501
4502 if( groupIdx >= 0 )
4503 {
4504 for( PAD* pad : m_pads )
4505 {
4506 if( padToNetTieGroupMap[ pad->GetNumber() ] == groupIdx )
4507 otherPads.push_back( pad );
4508 }
4509 }
4510
4511 return otherPads;
4512}
4513
4514
4516{
4517 wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );
4518
4519 FOOTPRINT* image = static_cast<FOOTPRINT*>( aImage );
4520
4521 std::swap( *this, *image );
4522
4524 [&]( BOARD_ITEM* child )
4525 {
4526 child->SetParent( this );
4527 },
4529
4530 image->RunOnChildren(
4531 [&]( BOARD_ITEM* child )
4532 {
4533 child->SetParent( image );
4534 },
4536}
4537
4538
4540{
4541 for( PAD* pad : Pads() )
4542 {
4543 if( pad->GetAttribute() != PAD_ATTRIB::SMD )
4544 return true;
4545 }
4546
4547 return false;
4548}
4549
4550
4551bool FOOTPRINT::operator==( const BOARD_ITEM& aOther ) const
4552{
4553 if( aOther.Type() != PCB_FOOTPRINT_T )
4554 return false;
4555
4556 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
4557
4558 return *this == other;
4559}
4560
4561
4562bool FOOTPRINT::operator==( const FOOTPRINT& aOther ) const
4563{
4564 auto equalItems =
4565 []( const auto* aLeft, const auto* aRight )
4566 {
4567 return *aLeft == *aRight;
4568 };
4569
4570 if( !std::ranges::equal( m_pads, aOther.m_pads, equalItems ) )
4571 return false;
4572
4573 if( !std::ranges::equal( m_drawings, aOther.m_drawings, equalItems ) )
4574 return false;
4575
4576 if( !std::ranges::equal( m_zones, aOther.m_zones, equalItems ) )
4577 return false;
4578
4579 if( !std::ranges::equal( m_groups, aOther.m_groups, equalItems ) )
4580 return false;
4581
4582 if( m_netTiePadGroups != aOther.m_netTiePadGroups )
4583 return false;
4584
4585 if( m_jumperPadGroups != aOther.m_jumperPadGroups )
4586 return false;
4587
4588 if( m_points.size() != aOther.m_points.size() )
4589 return false;
4590
4591 if( !std::ranges::equal( m_bodies, aOther.m_bodies, equalItems ) )
4592 return false;
4593
4594 if( m_height != aOther.m_height )
4595 return false;
4596
4597 // Compare fields in ordinally-sorted order
4598 std::vector<PCB_FIELD*> fields, otherFields;
4599
4600 GetFields( fields, false );
4601 aOther.GetFields( otherFields, false );
4602
4603 if( fields.size() != otherFields.size() )
4604 return false;
4605
4606 for( size_t ii = 0; ii < fields.size(); ++ii )
4607 {
4608 if( fields[ii] )
4609 {
4610 if( !( *fields[ii] == *otherFields[ii] ) )
4611 return false;
4612 }
4613 }
4614
4615 return true;
4616}
4617
4618
4619double FOOTPRINT::Similarity( const BOARD_ITEM& aOther ) const
4620{
4621 if( aOther.Type() != PCB_FOOTPRINT_T )
4622 return 0.0;
4623
4624 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
4625
4626 double similarity = 1.0;
4627
4628 for( const PAD* pad : m_pads)
4629 {
4630 const PAD* otherPad = other.FindPadByNumber( pad->GetNumber() );
4631
4632 if( !otherPad )
4633 continue;
4634
4635 similarity *= pad->Similarity( *otherPad );
4636 }
4637
4638 return similarity;
4639}
4640
4641
4645static constexpr std::optional<bool> cmp_points_opt( const VECTOR2I& aPtA, const VECTOR2I& aPtB )
4646{
4647 if( aPtA.x != aPtB.x )
4648 return aPtA.x < aPtB.x;
4649
4650 if( aPtA.y != aPtB.y )
4651 return aPtA.y < aPtB.y;
4652
4653 return std::nullopt;
4654}
4655
4656
4657bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* itemA, const BOARD_ITEM* itemB ) const
4658{
4659 if( itemA->Type() != itemB->Type() )
4660 return itemA->Type() < itemB->Type();
4661
4662 if( itemA->GetLayer() != itemB->GetLayer() )
4663 return itemA->GetLayer() < itemB->GetLayer();
4664
4665 switch( itemA->Type() )
4666 {
4667 case PCB_SHAPE_T:
4668 {
4669 const PCB_SHAPE* dwgA = static_cast<const PCB_SHAPE*>( itemA );
4670 const PCB_SHAPE* dwgB = static_cast<const PCB_SHAPE*>( itemB );
4671
4672 if( dwgA->GetShape() != dwgB->GetShape() )
4673 return dwgA->GetShape() < dwgB->GetShape();
4674
4675 if( dwgA->GetShape() != SHAPE_T::POLY )
4676 {
4677 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetLibraryStart(), dwgB->GetLibraryStart() ) )
4678 return *cmp;
4679
4680 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetLibraryEnd(), dwgB->GetLibraryEnd() ) )
4681 return *cmp;
4682 }
4683
4684 if( dwgA->GetShape() == SHAPE_T::ARC )
4685 {
4686 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetLibraryArcMid(), dwgB->GetLibraryArcMid() ) )
4687 return *cmp;
4688 }
4689 else if( dwgA->GetShape() == SHAPE_T::BEZIER )
4690 {
4691 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetLibraryBezierC1(), dwgB->GetLibraryBezierC1() ) )
4692 return *cmp;
4693
4694 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetLibraryBezierC2(), dwgB->GetLibraryBezierC2() ) )
4695 return *cmp;
4696 }
4697 else if( dwgA->GetShape() == SHAPE_T::POLY )
4698 {
4699 const SHAPE_POLY_SET aLib = dwgA->GetLibraryPolyShape();
4700 const SHAPE_POLY_SET bLib = dwgB->GetLibraryPolyShape();
4701
4702 if( aLib.TotalVertices() != bLib.TotalVertices() )
4703 return aLib.TotalVertices() < bLib.TotalVertices();
4704
4705 for( int ii = 0; ii < aLib.TotalVertices(); ++ii )
4706 {
4707 if( std::optional<bool> cmp = cmp_points_opt( aLib.CVertex( ii ), bLib.CVertex( ii ) ) )
4708 return *cmp;
4709 }
4710 }
4711 else if( dwgA->GetShape() == SHAPE_T::ELLIPSE || dwgA->GetShape() == SHAPE_T::ELLIPSE_ARC )
4712 {
4713 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetEllipseCenter(), dwgB->GetEllipseCenter() ) )
4714 return *cmp;
4715
4716 if( dwgA->GetEllipseMajorRadius() != dwgB->GetEllipseMajorRadius() )
4717 return dwgA->GetEllipseMajorRadius() < dwgB->GetEllipseMajorRadius();
4718
4719 if( dwgA->GetEllipseMinorRadius() != dwgB->GetEllipseMinorRadius() )
4720 return dwgA->GetEllipseMinorRadius() < dwgB->GetEllipseMinorRadius();
4721
4724
4725 if( dwgA->GetShape() == SHAPE_T::ELLIPSE_ARC )
4726 {
4731
4733 return dwgA->GetEllipseEndAngle().AsTenthsOfADegree()
4735 }
4736 }
4737
4738 if( dwgA->GetWidth() != dwgB->GetWidth() )
4739 return dwgA->GetWidth() < dwgB->GetWidth();
4740
4741 break;
4742 }
4743 case PCB_TEXT_T:
4744 {
4745 const PCB_TEXT& textA = static_cast<const PCB_TEXT&>( *itemA );
4746 const PCB_TEXT& textB = static_cast<const PCB_TEXT&>( *itemB );
4747
4748 if( std::optional<bool> cmp = cmp_points_opt( textA.GetFPRelativePosition(), textB.GetFPRelativePosition() ) )
4749 return *cmp;
4750
4751 if( textA.GetTextAngle() != textB.GetTextAngle() )
4752 return textA.GetTextAngle() < textB.GetTextAngle();
4753
4754 if( std::optional<bool> cmp = cmp_points_opt( textA.GetTextSize(), textB.GetTextSize() ) )
4755 return *cmp;
4756
4757 if( textA.GetTextThickness() != textB.GetTextThickness() )
4758 return textA.GetTextThickness() < textB.GetTextThickness();
4759
4760 if( textA.IsBold() != textB.IsBold() )
4761 return textA.IsBold() < textB.IsBold();
4762
4763 if( textA.IsItalic() != textB.IsItalic() )
4764 return textA.IsItalic() < textB.IsItalic();
4765
4766 if( textA.IsMirrored() != textB.IsMirrored() )
4767 return textA.IsMirrored() < textB.IsMirrored();
4768
4769 if( textA.GetLineSpacing() != textB.GetLineSpacing() )
4770 return textA.GetLineSpacing() < textB.GetLineSpacing();
4771
4772 if( textA.GetText() != textB.GetText() )
4773 return textA.GetText().Cmp( textB.GetText() ) < 0;
4774
4775 break;
4776 }
4777 default:
4778 {
4779 // These items don't have their own specific sorting criteria.
4780 break;
4781 }
4782 }
4783
4784 if( itemA->m_Uuid != itemB->m_Uuid )
4785 return itemA->m_Uuid < itemB->m_Uuid;
4786
4787 return itemA < itemB;
4788}
4789
4790
4791bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) const
4792{
4793 if( aFirst->GetNumber() != aSecond->GetNumber() )
4794 return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0;
4795
4796 if( std::optional<bool> cmp = cmp_points_opt( aFirst->GetFPRelativePosition(), aSecond->GetFPRelativePosition() ) )
4797 return *cmp;
4798
4799 std::optional<bool> padCopperMatches;
4800
4801 // Pick the "most complex" padstack to iterate
4802 const PAD* checkPad = aFirst;
4803
4804 if( aSecond->Padstack().Mode() == PADSTACK::MODE::CUSTOM
4805 || ( aSecond->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK &&
4806 aFirst->Padstack().Mode() == PADSTACK::MODE::NORMAL ) )
4807 {
4808 checkPad = aSecond;
4809 }
4810
4811 checkPad->Padstack().ForEachUniqueLayer(
4812 [&]( PCB_LAYER_ID aLayer )
4813 {
4814 if( aFirst->GetSize( aLayer ).x != aSecond->GetSize( aLayer ).x )
4815 padCopperMatches = aFirst->GetSize( aLayer ).x < aSecond->GetSize( aLayer ).x;
4816 else if( aFirst->GetSize( aLayer ).y != aSecond->GetSize( aLayer ).y )
4817 padCopperMatches = aFirst->GetSize( aLayer ).y < aSecond->GetSize( aLayer ).y;
4818 else if( aFirst->GetShape( aLayer ) != aSecond->GetShape( aLayer ) )
4819 padCopperMatches = aFirst->GetShape( aLayer ) < aSecond->GetShape( aLayer );
4820 } );
4821
4822 if( padCopperMatches.has_value() )
4823 return *padCopperMatches;
4824
4825 if( aFirst->GetLayerSet() != aSecond->GetLayerSet() )
4826 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
4827
4828 if( aFirst->m_Uuid != aSecond->m_Uuid )
4829 return aFirst->m_Uuid < aSecond->m_Uuid;
4830
4831 return aFirst < aSecond;
4832}
4833
4834
4835#if 0
4836bool FOOTPRINT::cmp_padstack::operator()( const PAD* aFirst, const PAD* aSecond ) const
4837{
4838 if( aFirst->GetSize().x != aSecond->GetSize().x )
4839 return aFirst->GetSize().x < aSecond->GetSize().x;
4840 if( aFirst->GetSize().y != aSecond->GetSize().y )
4841 return aFirst->GetSize().y < aSecond->GetSize().y;
4842
4843 if( aFirst->GetShape() != aSecond->GetShape() )
4844 return aFirst->GetShape() < aSecond->GetShape();
4845
4846 if( aFirst->GetLayerSet() != aSecond->GetLayerSet() )
4847 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
4848
4849 if( aFirst->GetDrillSize().x != aSecond->GetDrillSize().x )
4850 return aFirst->GetDrillSize().x < aSecond->GetDrillSize().x;
4851
4852 if( aFirst->GetDrillSize().y != aSecond->GetDrillSize().y )
4853 return aFirst->GetDrillSize().y < aSecond->GetDrillSize().y;
4854
4855 if( aFirst->GetDrillShape() != aSecond->GetDrillShape() )
4856 return aFirst->GetDrillShape() < aSecond->GetDrillShape();
4857
4858 if( aFirst->GetAttribute() != aSecond->GetAttribute() )
4859 return aFirst->GetAttribute() < aSecond->GetAttribute();
4860
4861 if( aFirst->GetOrientation() != aSecond->GetOrientation() )
4862 return aFirst->GetOrientation() < aSecond->GetOrientation();
4863
4864 if( aFirst->GetSolderMaskExpansion() != aSecond->GetSolderMaskExpansion() )
4865 return aFirst->GetSolderMaskExpansion() < aSecond->GetSolderMaskExpansion();
4866
4867 if( aFirst->GetSolderPasteMargin() != aSecond->GetSolderPasteMargin() )
4868 return aFirst->GetSolderPasteMargin() < aSecond->GetSolderPasteMargin();
4869
4870 if( aFirst->GetLocalSolderMaskMargin() != aSecond->GetLocalSolderMaskMargin() )
4871 return aFirst->GetLocalSolderMaskMargin() < aSecond->GetLocalSolderMaskMargin();
4872
4873 const std::shared_ptr<SHAPE_POLY_SET>& firstShape = aFirst->GetEffectivePolygon( ERROR_INSIDE );
4874 const std::shared_ptr<SHAPE_POLY_SET>& secondShape = aSecond->GetEffectivePolygon( ERROR_INSIDE );
4875
4876 if( firstShape->VertexCount() != secondShape->VertexCount() )
4877 return firstShape->VertexCount() < secondShape->VertexCount();
4878
4879 for( int ii = 0; ii < firstShape->VertexCount(); ++ii )
4880 {
4881 if( std::optional<bool> cmp = cmp_points_opt( firstShape->CVertex( ii ), secondShape->CVertex( ii ) ) )
4882 {
4883 return *cmp;
4884 }
4885 }
4886
4887 return false;
4888}
4889#endif
4890
4891
4892bool FOOTPRINT::cmp_zones::operator()( const ZONE* aFirst, const ZONE* aSecond ) const
4893{
4894 if( aFirst->GetAssignedPriority() != aSecond->GetAssignedPriority() )
4895 return aFirst->GetAssignedPriority() < aSecond->GetAssignedPriority();
4896
4897 if( aFirst->GetLayerSet() != aSecond->GetLayerSet() )
4898 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
4899
4900 const SHAPE_POLY_SET aLib = aFirst->GetLibraryOutline();
4901 const SHAPE_POLY_SET bLib = aSecond->GetLibraryOutline();
4902
4903 if( aLib.TotalVertices() != bLib.TotalVertices() )
4904 return aLib.TotalVertices() < bLib.TotalVertices();
4905
4906 for( int ii = 0; ii < aLib.TotalVertices(); ++ii )
4907 {
4908 if( std::optional<bool> cmp = cmp_points_opt( aLib.CVertex( ii ), bLib.CVertex( ii ) ) )
4909 return *cmp;
4910 }
4911
4912 if( aFirst->m_Uuid != aSecond->m_Uuid )
4913 return aFirst->m_Uuid < aSecond->m_Uuid;
4914
4915 return aFirst < aSecond;
4916}
4917
4918
4920 int aMaxError, ERROR_LOC aErrorLoc ) const
4921{
4922 auto processPad =
4923 [&]( const PAD* pad, PCB_LAYER_ID padLayer )
4924 {
4925 VECTOR2I clearance( aClearance, aClearance );
4926
4927 switch( aLayer )
4928 {
4929 case F_Mask:
4930 case B_Mask:
4931 clearance.x += pad->GetSolderMaskExpansion( padLayer );
4932 clearance.y += pad->GetSolderMaskExpansion( padLayer );
4933 break;
4934
4935 case F_Paste:
4936 case B_Paste:
4937 clearance += pad->GetSolderPasteMargin( padLayer );
4938 break;
4939
4940 default:
4941 break;
4942 }
4943
4944 // Our standard TransformShapeToPolygon() routines can't handle differing x:y clearance
4945 // values (which get generated when a relative paste margin is used with an oblong pad).
4946 // So we apply this huge hack and fake a larger pad to run the transform on.
4947 // Of course being a hack it falls down when dealing with custom shape pads (where the
4948 // size is only the size of the anchor), so for those we punt and just use clearance.x.
4949
4950 if( ( clearance.x < 0 || clearance.x != clearance.y )
4951 && pad->GetShape( padLayer ) != PAD_SHAPE::CUSTOM )
4952 {
4953 VECTOR2I dummySize = pad->GetSize( padLayer ) + clearance + clearance;
4954
4955 if( dummySize.x <= 0 || dummySize.y <= 0 )
4956 return;
4957
4958 PAD dummy( *pad );
4959 dummy.SetSize( padLayer, dummySize );
4960 dummy.TransformShapeToPolygon( aBuffer, padLayer, 0, aMaxError, aErrorLoc );
4961 }
4962 else
4963 {
4964 pad->TransformShapeToPolygon( aBuffer, padLayer, clearance.x, aMaxError, aErrorLoc );
4965 }
4966 };
4967
4968 for( const PAD* pad : m_pads )
4969 {
4970 if( !pad->FlashLayer( aLayer ) )
4971 continue;
4972
4973 if( aLayer == UNDEFINED_LAYER )
4974 {
4975 pad->Padstack().ForEachUniqueLayer(
4976 [&]( PCB_LAYER_ID l )
4977 {
4978 processPad( pad, l );
4979 } );
4980 }
4981 else
4982 {
4983 processPad( pad, aLayer );
4984 }
4985 }
4986}
4987
4988
4990 int aError, ERROR_LOC aErrorLoc, bool aIncludeText,
4991 bool aIncludeShapes, bool aIncludePrivateItems ) const
4992{
4993 for( BOARD_ITEM* item : GraphicalItems() )
4994 {
4995 if( GetPrivateLayers().test( item->GetLayer() ) && !aIncludePrivateItems )
4996 continue;
4997
4998 if( item->Type() == PCB_TEXT_T && aIncludeText )
4999 {
5000 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
5001
5002 if( aLayer == UNDEFINED_LAYER || text->GetLayer() == aLayer )
5003 text->TransformTextToPolySet( aBuffer, aClearance, aError, aErrorLoc );
5004 }
5005
5006 if( item->Type() == PCB_TEXTBOX_T && aIncludeText )
5007 {
5008 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
5009
5010 if( aLayer == UNDEFINED_LAYER || textbox->GetLayer() == aLayer )
5011 {
5012 // border
5013 if( textbox->IsBorderEnabled() )
5014 textbox->PCB_SHAPE::TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
5015
5016 // text
5017 textbox->TransformTextToPolySet( aBuffer, 0, aError, aErrorLoc );
5018 }
5019 }
5020
5021 if( item->Type() == PCB_SHAPE_T && aIncludeShapes )
5022 {
5023 const PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
5024
5025 if( aLayer == UNDEFINED_LAYER || shape->GetLayer() == aLayer )
5026 shape->TransformShapeToPolySet( aBuffer, aLayer, 0, aError, aErrorLoc );
5027 }
5028
5029 if( item->Type() == PCB_BARCODE_T && aIncludeShapes )
5030 {
5031 const PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( item );
5032
5033 if( aLayer == UNDEFINED_LAYER || barcode->GetLayer() == aLayer )
5034 barcode->TransformShapeToPolySet( aBuffer, aLayer, 0, aError, aErrorLoc );
5035 }
5036 }
5037
5038 if( aIncludeText )
5039 {
5040 for( const PCB_FIELD* field : m_fields )
5041 {
5042 if( ( aLayer == UNDEFINED_LAYER || field->GetLayer() == aLayer ) && field->IsVisible() )
5043 field->TransformTextToPolySet( aBuffer, aClearance, aError, aErrorLoc );
5044 }
5045 }
5046}
5047
5048
5049std::set<KIFONT::OUTLINE_FONT*> FOOTPRINT::GetFonts() const
5050{
5052
5053 std::set<KIFONT::OUTLINE_FONT*> fonts;
5054
5055 auto processItem =
5056 [&]( BOARD_ITEM* item )
5057 {
5058 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
5059 {
5060 KIFONT::FONT* font = text->GetFont();
5061
5062 if( font && font->IsOutline() )
5063 {
5064 KIFONT::OUTLINE_FONT* outlineFont = static_cast<KIFONT::OUTLINE_FONT*>( font );
5065 PERMISSION permission = outlineFont->GetEmbeddingPermission();
5066
5067 if( permission == PERMISSION::EDITABLE || permission == PERMISSION::INSTALLABLE )
5068 fonts.insert( outlineFont );
5069 }
5070 }
5071 };
5072
5073 for( BOARD_ITEM* item : GraphicalItems() )
5074 processItem( item );
5075
5076 for( PCB_FIELD* field : GetFields() )
5077 processItem( field );
5078
5079 return fonts;
5080}
5081
5082
5084{
5085 for( KIFONT::OUTLINE_FONT* font : GetFonts() )
5086 {
5087 EMBEDDED_FILES::EMBEDDED_FILE* file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
5088
5089 if( !file )
5090 {
5091 wxLogTrace( "EMBED", "Failed to add font file: %s", font->GetFileName() );
5092 continue;
5093 }
5094
5096 }
5097}
5098
5099
5101{
5102 m_componentClassCacheProxy->SetStaticComponentClass( aClass );
5103}
5104
5105
5107{
5108 return m_componentClassCacheProxy->GetStaticComponentClass();
5109}
5110
5111
5113{
5114 m_componentClassCacheProxy->RecomputeComponentClass();
5115}
5116
5117
5119{
5120 return m_componentClassCacheProxy->GetComponentClass();
5121}
5122
5123
5125{
5126 if( !m_componentClassCacheProxy->GetComponentClass()->IsEmpty() )
5127 return m_componentClassCacheProxy->GetComponentClass()->GetName();
5128
5129 return wxEmptyString;
5130}
5131
5132
5134 const std::unordered_set<wxString>& aComponentClassNames )
5135{
5136 const COMPONENT_CLASS* componentClass =
5137 aBoard->GetComponentClassManager().GetEffectiveStaticComponentClass( aComponentClassNames );
5138 SetStaticComponentClass( componentClass );
5139}
5140
5141
5143{
5144 m_componentClassCacheProxy->InvalidateCache();
5145}
5146
5147
5149{
5150 m_stackupMode = aMode;
5151
5153 {
5154 // Reset the stackup layers to the default values
5156 }
5157}
5158
5159
5161{
5162 wxCHECK2( m_stackupMode == FOOTPRINT_STACKUP::CUSTOM_LAYERS, /*void*/ );
5163
5165 m_stackupLayers = std::move( aLayers );
5166}
5167
5168
5170{
5171 if( !aBoard )
5172 return;
5173
5175 return;
5176
5177 const LSET boardCopper = LSET::AllCuMask( aBoard->GetCopperLayerCount() );
5178
5179 for( PAD* pad : Pads() )
5180 {
5181 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
5182 {
5183 LSET padLayers = pad->GetLayerSet();
5184 padLayers |= boardCopper;
5185 pad->SetLayerSet( padLayers );
5186 }
5187 }
5188}
5189
5190
5191static struct FOOTPRINT_DESC
5192{
5194 {
5196 .Map( FOOTPRINT_TYPE::THROUGH_HOLE, _HKI( "Through hole" ) )
5197 .Map( FOOTPRINT_TYPE::SMD, _HKI( "SMD" ) )
5198 .Map( FOOTPRINT_TYPE::UNSPECIFIED, _HKI( "Unspecified" ) );
5199
5201
5202 if( zcMap.Choices().GetCount() == 0 )
5203 {
5205 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
5206 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
5207 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
5208 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
5209 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
5210 }
5211
5213
5214 if( layerEnum.Choices().GetCount() == 0 )
5215 {
5216 layerEnum.Undefined( UNDEFINED_LAYER );
5217
5218 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
5219 layerEnum.Map( layer, LSET::Name( layer ) );
5220 }
5221
5222 wxPGChoices fpLayers; // footprints might be placed only on F.Cu & B.Cu
5223 fpLayers.Add( LSET::Name( F_Cu ), F_Cu );
5224 fpLayers.Add( LSET::Name( B_Cu ), B_Cu );
5225
5232
5233 auto isNotFootprintHolder =
5234 []( INSPECTABLE* aItem ) -> bool
5235 {
5236 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aItem ) )
5237 {
5238 if( BOARD* board = footprint->GetBoard() )
5239 return !board->IsFootprintHolder();
5240 }
5241 return true;
5242 };
5243
5244 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ),
5247 .SetAvailableFunc( isNotFootprintHolder )
5248 .SetChoices( fpLayers );
5249
5250 propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Orientation" ),
5253 .SetAvailableFunc( isNotFootprintHolder );
5254
5255 propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Scale X" ),
5257 .SetAvailableFunc( isNotFootprintHolder );
5258
5259 propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Scale Y" ),
5261 .SetAvailableFunc( isNotFootprintHolder );
5262
5263 const wxString groupFields = _HKI( "Fields" );
5264
5265 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Reference" ),
5267 groupFields );
5268
5269 const wxString propertyFields = _HKI( "Footprint Properties" );
5270
5271 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Link" ),
5273 propertyFields );
5274 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Description" ),
5276 propertyFields );
5277 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Keywords" ),
5279 propertyFields );
5280
5281 propMgr.AddProperty( new PROPERTY<FOOTPRINT, int>( _HKI( "Height" ),
5283 propertyFields ).SetIsCopyable();
5284
5285 // Note: Also used by DRC engine
5286 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Component Class" ),
5288 propertyFields )
5290
5291 const wxString groupAttributes = _HKI( "Attributes" );
5292
5293 propMgr.AddProperty( new PROPERTY_ENUM<FOOTPRINT, FOOTPRINT_TYPE>( _HKI( "Footprint Type" ),
5295 groupAttributes );
5296 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Not in Schematic" ),
5297 &FOOTPRINT::SetBoardOnly, &FOOTPRINT::IsBoardOnly ), groupAttributes );
5298 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Position Files" ),
5300 groupAttributes );
5301 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Bill of Materials" ),
5303 groupAttributes );
5304 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Simulation" ),
5306 groupAttributes );
5307 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Do not Populate" ),
5309 groupAttributes );
5310
5311 const wxString groupOverrides = _HKI( "Overrides" );
5312
5313 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exempt From Courtyard Requirement" ),
5315 groupOverrides );
5316 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>( _HKI( "Clearance Override" ),
5318 groupOverrides ).SetIsCopyable();
5319 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>( _HKI( "Solderpaste Margin Override" ),
5322 groupOverrides ).SetIsCopyable();
5323 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<double>>( _HKI( "Solderpaste Margin Ratio Override" ),
5326 groupOverrides ).SetIsCopyable();
5327 propMgr.AddProperty( new PROPERTY_ENUM<FOOTPRINT, ZONE_CONNECTION>( _HKI( "Zone Connection Style" ),
5329 groupOverrides ).SetIsCopyable();
5330 }
5332
const char * name
KICOMMON_API types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICOMMON_API KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:55
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...
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr int ARC_LOW_DEF
Definition base_units.h:143
BITMAPS
A list of all bitmap identifiers.
@ FPHOLDER
Definition board.h:403
#define DEFAULT_COURTYARD_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
BASE_SET & set(size_t pos)
Definition base_set.h:126
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Abstract interface for BOARD_ITEMs capable of storing other items inside.
BOARD_ITEM_CONTAINER(BOARD_ITEM *aParent, KICAD_T aType)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:87
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
void ResetUuidDirect()
Definition board_item.h:278
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
void SetLocked(bool aLocked) override
Definition board_item.h:427
PCB_LAYER_ID GetFlippedLayer(PCB_LAYER_ID aLayer) const
Return the layer paired with aLayer on the opposite side of the board.
static double polygonArea(SHAPE_POLY_SET &aPolySet)
Area of aPolySet with every contour closed first, holes subtracted.
PCB_LAYER_ID m_layer
Definition board_item.h:600
bool IsLocked() const override
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
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.
void SetX(int aX)
Definition board_item.h:156
void SetY(int aY)
Definition board_item.h:162
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:383
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
VECTOR2I GetFPRelativePosition() const
virtual void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, KIGFX::RENDER_SETTINGS *aRenderSettings=nullptr) const
Convert the item shape to a polyset.
Definition board_item.h:560
BOARD_ITEM & operator=(const BOARD_ITEM &aOther)
Definition board_item.h:104
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:267
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
bool IsFootprintHolder() const
Find out if the board is being used to hold a single footprint for editing/viewing.
Definition board.h:441
int GetCopperLayerCount() const
Definition board.cpp:1127
wxString GetCurrentVariant() const
Definition board.h:530
COMPONENT_CLASS_MANAGER & GetComponentClassManager()
Gets the component class manager.
Definition board.h:1684
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
constexpr coord_type GetBottom() const
Definition box2.h:219
virtual int Accuracy() const =0
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
const COMPONENT_CLASS * GetStaticComponentClass() const
Gets the static component class.
COMPONENT_CLASS * GetEffectiveStaticComponentClass(const std::unordered_set< wxString > &classNames)
Computes and returns an effective component class for a (possibly empty) set of constituent class nam...
A lightweight representation of a component class.
int AsTenthsOfADegree() const
Definition eda_angle.h:117
EDA_ANGLE Normalize180()
Definition eda_angle.h:273
bool IsType(FRAME_T aType) const
The base class for create windows for drawing purpose.
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
virtual void ClearEditFlags()
Definition eda_item.h:178
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
std::vector< PROPERTY_BASE * > GetCustomPropertiesAsInspectables() const
Definition eda_item.cpp:200
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
virtual bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const
Compare the item against the search criteria in aSearchData.
Definition eda_item.h:484
static INSPECT_RESULT IterateForward(std::deque< T > &aList, INSPECTOR inspector, void *testData, const std::vector< KICAD_T > &scanTypes)
This changes first parameter to avoid the DList and use the main queue instead.
Definition eda_item.h:401
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:168
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:84
int GetEllipseMinorRadius() const
Definition eda_shape.h:403
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:385
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:431
int GetEllipseMajorRadius() const
Definition eda_shape.h:394
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:412
SHAPE_T GetShape() const
Definition eda_shape.h:177
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:422
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
bool IsItalic() const
Definition eda_text.h:200
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual bool IsVisible() const
Definition eda_text.h:226
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
double GetLineSpacing() const
Definition eda_text.h:298
bool IsMirrored() const
Definition eda_text.h:229
bool IsBold() const
Definition eda_text.h:215
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
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.
EMBEDDED_FILES()=default
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:798
static ENUM_MAP< T > & Instance()
Definition property.h:792
ENUM_MAP & Undefined(T aValue)
Definition property.h:805
wxPGChoices & Choices()
Definition property.h:843
Variant information for a footprint.
Definition footprint.h:178
wxString GetName() const
Definition footprint.h:189
bool HasFieldValue(const wxString &aFieldName) const
Definition footprint.h:237
void SetExcludedFromPosFiles(bool aExclude)
Definition footprint.h:202
wxString GetFieldValue(const wxString &aFieldName) const
Get a field value override for this variant.
Definition footprint.h:209
void SetName(const wxString &aName)
Definition footprint.h:190
bool GetExcludedFromSim() const
Definition footprint.h:198
bool GetExcludedFromBOM() const
Definition footprint.h:195
void SetExcludedFromSim(bool aExclude)
Definition footprint.h:199
void SetDNP(bool aDNP)
Definition footprint.h:193
bool GetExcludedFromPosFiles() const
Definition footprint.h:201
bool GetDNP() const
Definition footprint.h:192
void SetFieldValue(const wxString &aFieldName, const wxString &aValue)
Set a field value override for this variant.
Definition footprint.h:224
void SetExcludedFromBOM(bool aExclude)
Definition footprint.h:196
bool FixUuids()
Old footprints do not always have a valid UUID (some can be set to null uuid) However null UUIDs,...
bool GetDuplicatePadNumbersAreJumpers() const
Definition footprint.h:1127
double GetCoverageArea(int aTextMargin) const override
Return the board area this item covers, used to rank candidates under the cursor so a click on a smal...
void EmbedFonts() override
bool AllowSolderMaskBridges() const
Definition footprint.h:518
void SetPosition(const VECTOR2I &aPos) override
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:418
LIB_ID m_fpid
Definition footprint.h:1388
wxString GetLibDescription() const
Definition footprint.h:449
ZONE_CONNECTION GetLocalZoneConnection() const
Definition footprint.h:491
KIID_PATH m_path
Definition footprint.h:1445
std::deque< BOARD_ITEM * > m_drawings
Definition footprint.h:1379
void SetStackupLayers(LSET aLayers)
If the footprint has a non-default stackup, set the layers that should be used for the stackup.
bool IsBoardOnly() const
Definition footprint.h:911
void SerializeDefinition(kiapi::board::types::Footprint *aOutput) const
void InvalidateComponentClassCache() const
Forces deferred (on next access) recalculation of the component class for this footprint.
bool IsDNP() const
Definition footprint.h:947
void SetFootprintType(FOOTPRINT_TYPE aFootprintType)
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
EDA_ANGLE GetOrientation() const
Definition footprint.h:382
PCB_3D_BODY & EnsureBody()
ZONES & Zones()
Definition footprint.h:361
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
std::deque< PCB_POINT * > m_points
Definition footprint.h:1384
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
ZONE_CONNECTION m_zoneConnection
Definition footprint.h:1432
int GetNextFieldOrdinal() const
Return the next ordinal for a user field for this footprint.
PCB_POINTS & Points()
Definition footprint.h:370
bool IsExcludedFromBOM() const
Definition footprint.h:929
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
void SetOrientation(const EDA_ANGLE &aNewAngle)
std::optional< double > m_solderPasteMarginRatio
Definition footprint.h:1436
void SetDNP(bool aDNP=true)
Definition footprint.h:948
void SetAllowSolderMaskBridges(bool aAllow)
Definition footprint.h:519
void RecomputeComponentClass() const
Forces immediate recalculation of the component class for this footprint.
std::vector< ZONE * > m_zones
Definition footprint.h:1381
bool GetExcludedFromSimForVariant(const wxString &aVariantName) const
Get the exclude-from-simulation status for a specific variant.
static bool IsLibNameValid(const wxString &aName)
Test for validity of a name of a footprint to be used in a footprint library ( no spaces,...
void SetStackupMode(FOOTPRINT_STACKUP aMode)
Set the stackup mode for this footprint.
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition footprint.h:488
std::vector< SEARCH_TERM > & GetSearchTerms() override
double ViewGetLOD(int aLayer, const KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
std::optional< int > m_clearance
Definition footprint.h:1433
bool m_duplicatePadNumbersAreJumpers
Flag that this footprint should automatically treat sets of two or more pads with the same number as ...
Definition footprint.h:1426
void SetExcludedFromSim(bool aExclude=true)
Definition footprint.h:939
void SetExcludedFromBOM(bool aExclude=true)
Definition footprint.h:930
int m_fpStatus
Definition footprint.h:1390
std::map< wxString, std::unique_ptr< PCB_FOOTPRINT_FIELD_PROPERTY > > m_fieldPropsStorageCache
Per-object cache of dynamic property descriptors for custom fields, keyed by descriptor name and popu...
Definition footprint.h:1411
void SetKeywords(const wxString &aKeywords)
Definition footprint.h:453
void SetStaticComponentClass(const COMPONENT_CLASS *aClass) const
Sets the component class object pointer for this footprint.
wxString DisambiguateItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
int m_attributes
Definition footprint.h:1389
PCB_LAYER_ID GetSide() const
Use instead of IsFlipped() when you also need to account for unsided footprints (those purely on user...
wxString GetLibNickname() const override
Definition footprint.h:444
const BOX2I GetLayerBoundingBox(const LSET &aLayers) const
Return the bounding box of the footprint on a given set of layers.
std::vector< FP_3DMODEL > m_3D_Drawings
Definition footprint.h:1453
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...
FOOTPRINT_TYPE GetFootprintType() const
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
bool m_allowMissingCourtyard
Definition footprint.h:1428
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
std::optional< int > GetLocalSolderPasteMargin() const
Definition footprint.h:484
std::unique_ptr< COMPONENT_CLASS_CACHE_PROXY > m_componentClassCacheProxy
Definition footprint.h:1463
wxArrayString * m_initial_comments
Definition footprint.h:1456
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
BOX2I GetFpPadsLocalBbox() const
Return the bounding box containing pads when the footprint is on the front side, orientation 0,...
LIB_ID GetLIB_ID() const override
Definition footprint.h:442
std::deque< PCB_FIELD * > m_fields
Definition footprint.h:1378
std::mutex m_geometry_cache_mutex
Definition footprint.h:1406
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:831
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
std::optional< int > m_solderPasteMargin
Definition footprint.h:1435
std::mutex m_courtyard_cache_mutex
Definition footprint.h:1460
void SetExcludedFromPosFiles(bool aExclude=true)
Definition footprint.h:921
bool IsWithinSchematicSheet(const KIID_PATH &aSheetPath) const
Test whether this footprint's symbol lives on aSheetPath or any sheet below it.
void SetOrientationDegrees(double aOrientation)
Definition footprint.h:408
unsigned GetPadCount() const
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
std::optional< int > GetLocalClearance() const
Definition footprint.h:478
void UpdateFields(const std::vector< PCB_FIELD > &aFields, std::vector< PCB_FIELD * > &aAdded, std::vector< PCB_FIELD * > &aDetached)
Replace the fields with aFields, reusing the existing mandatory field objects.
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
const FOOTPRINT_VARIANT * GetVariant(const wxString &aVariantName) const
Get a variant by name.
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
void SetDuplicatePadNumbersAreJumpers(bool aEnabled)
Definition footprint.h:1128
bool m_allowSolderMaskBridges
Definition footprint.h:1429
FOOTPRINT & operator=(const FOOTPRINT &aOther)
bool HasField(const wxString &aFieldName) const
CONSTRAINTS & Constraints()
Definition footprint.h:367
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
void SetScaleY(double aScaleY)
Definition footprint.h:404
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.
double GetOrientationDegrees() const
Definition footprint.h:412
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...
std::deque< PAD * > & Pads()
Definition footprint.h:355
void ResolveComponentClassNames(BOARD *aBoard, const std::unordered_set< wxString > &aComponentClassNames)
Resolves a set of component class names to this footprint's actual component class.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const override
Compare the item against the search criteria in aSearchData.
const COMPONENT_CLASS * GetComponentClass() const
Returns the component class for this footprint.
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition footprint.h:490
BOARD_ITEM * DuplicateItem(bool addToParentGroup, BOARD_COMMIT *aCommit, const BOARD_ITEM *aItem, bool addToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
FOOTPRINT(BOARD *parent)
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:393
bool ResolveTextVar(wxString *aToken, int aDepth=0) const
Resolve any references to system tokens supported by the component.
LSET GetPrivateLayers() const
Definition footprint.h:295
wxString GetFPIDAsString() const
Definition footprint.h:419
CASE_INSENSITIVE_MAP< FOOTPRINT_VARIANT > m_variants
Variant data for this footprint, keyed by variant name.
Definition footprint.h:1394
double GetScaleX() const
Definition footprint.h:399
bool AllowMissingCourtyard() const
Definition footprint.h:515
void DeleteVariant(const wxString &aVariantName)
Delete a variant by name.
wxString GetComponentClassAsString() const
Used for display in the properties panel.
JUMPER_GROUP_SET 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:1422
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
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...
bool m_flipped
Definition footprint.h:1387
std::deque< PCB_CONSTRAINT * > m_constraints
Definition footprint.h:1383
wxString GetTypeName() const
Get the type of footprint.
const std::vector< wxString > & GetNetTiePadGroups() const
Definition footprint.h:567
const PCB_3D_BODY * GetBody() const
Definition footprint.h:427
const LIB_ID & GetFPID() const
Definition footprint.h:417
int GetHeight() const
The overall height of the footprint, or 0 if not specified.
Definition footprint.h:438
bool DeserializeDefinition(const kiapi::board::types::Footprint &aInput)
void SetReference(const wxString &aReference)
Definition footprint.h:799
std::vector< FP_UNIT_INFO > m_unitInfo
Definition footprint.h:1466
bool IsExcludedFromPosFiles() const
Definition footprint.h:920
void SetLayerAndFlip(PCB_LAYER_ID aLayer)
Used as Layer property setter – performs a flip if necessary to set the footprint layer.
unsigned GetNumberedPadCount() const
Return the number of unique pads whose pad number represents an electrical pin.
void SetHeight(int aHeight)
Definition footprint.h:439
wxString GetFieldValueForVariant(const wxString &aVariantName, const wxString &aFieldName) const
Get a field value for a specific variant.
void AddNetTiePadGroup(const wxString &aGroup)
Definition footprint.h:574
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
LSET m_privateLayers
Definition footprint.h:1441
bool IsExcludedFromSim() const
Definition footprint.h:938
const std::deque< PCB_FIELD * > & GetFields() const
Return a reference to the deque holding the footprint's fields.
Definition footprint.h:869
int GetLikelyAttribute() const
Return the most likely attribute based on pads.
std::deque< PCB_GROUP * > m_groups
Definition footprint.h:1382
void Move(const VECTOR2I &aMoveVector) override
Move this object.
wxString m_libDescription
Definition footprint.h:1443
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.
void ApplyDefaultSettings(const BOARD &board, bool aStyleFields, bool aStyleText, bool aStyleShapes, bool aStyleDimensions, bool aStyleBarcodes)
Apply default board settings to the footprint field text properties.
std::vector< wxString > m_netTiePadGroups
Definition footprint.h:1415
void InvalidateGeometryCaches()
Resets the caches for this footprint, for example if it was modified via the API.
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
std::set< KIFONT::OUTLINE_FONT * > GetFonts() const override
Get a list of outline fonts referenced in the footprint.
void Add3DModel(FP_3DMODEL *a3DModel)
Add a3DModel definition to the end of the 3D model list.
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.
void SetTransformScale(double aScaleX, double aScaleY)
FOOTPRINT_STACKUP m_stackupMode
Definition footprint.h:1440
PCB_FIELD & Reference()
Definition footprint.h:832
wxString GetReferenceAsString() const
Definition footprint.h:802
JUMPER_GROUP_SET & JumperPadGroups()
Each jumper pad group is a set of pad numbers that should be treated as internally connected.
Definition footprint.h:1134
std::deque< PCB_3D_BODY * > m_bodies
Definition footprint.h:1452
wxString m_sheetfile
Definition footprint.h:1447
PAD * FindPadByUuid(const KIID &aUuid) const
void GetContextualTextVars(wxArrayString *aVars) const
Return the list of system text vars for this footprint.
std::optional< int > m_solderMaskMargin
Definition footprint.h:1434
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to the container.
std::vector< const PAD * > GetPads(const wxString &aPadNumber, const PAD *aIgnore=nullptr) const
void AutoPositionFields()
Position Reference and Value fields at the top and bottom of footprint's bounding box.
wxString m_keywords
Definition footprint.h:1444
void SetScaleX(double aScaleX)
Definition footprint.h:402
void ClearAllNets()
Clear (i.e.
std::deque< PAD * > m_pads
Definition footprint.h:1380
void RescaleAroundPoint(const VECTOR2I &aCenter, double aSx, double aSy)
bool HasThroughHolePads() const
void BuildCourtyardCaches(OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Build complex polygons of the courtyard areas from graphic items on the courtyard layers.
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
bool HitTestAccurate(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if a point is inside the bounding polygon of the footprint.
bool GetDNPForVariant(const wxString &aVariantName) const
Get the DNP status for a specific variant.
wxString GetClass() const override
Return the class name.
Definition footprint.h:1173
void SetVariant(const FOOTPRINT_VARIANT &aVariant)
Add or update a variant.
std::unique_ptr< FOOTPRINT_COURTYARD_CACHE_DATA > m_courtyard_cache
Definition footprint.h:1459
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void IncrementReference(int aDelta)
Bump the current reference by aDelta.
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition footprint.h:487
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
KIID m_link
Definition footprint.h:1451
GROUPS & Groups()
Definition footprint.h:364
std::set< wxString > GetUniquePadNumbers() const
A complex pad can be built with many pads having the same pad name to create a complex shape or fragm...
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
void SetAllowMissingCourtyard(bool aAllow)
Definition footprint.h:516
void BuildNetTieCache()
Cache the pads that are allowed to connect to each other in the footprint.
bool IsConflicting() const
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:375
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
void ClearBody()
const SHAPE_POLY_SET & GetCachedCourtyard(PCB_LAYER_ID aLayer) const
Return the cached courtyard area.
wxString GetName() const override
Definition footprint.h:443
static const wxChar * StringLibNameInvalidChars(bool aUserReadable)
Test for validity of the name in a library of the footprint ( no spaces, dir separators ....
void SetLibDescription(const wxString &aDesc)
Definition footprint.h:450
bool TextOnly() const
const COMPONENT_CLASS * GetStaticComponentClass() const
Returns the component class for this footprint.
void FixUpPadsForBoard(BOARD *aBoard)
Used post-loading of a footprint to adjust the layers on pads to match board inner layers.
bool GetExcludedFromPosFilesForVariant(const wxString &aVariantName) const
Get the exclude-from-position-files status for a specific variant.
FOOTPRINT_VARIANT * AddVariant(const wxString &aVariantName)
Add a new variant with the given name.
double GetArea(int aPadding=0) const
wxString m_filters
Definition footprint.h:1448
const wxString & GetReference() const
Definition footprint.h:793
std::unique_ptr< FOOTPRINT_GEOMETRY_CACHE_DATA > m_geometry_cache
Definition footprint.h:1407
bool GetExcludedFromBOMForVariant(const wxString &aVariantName) const
Get the exclude-from-BOM status for a specific variant.
void CopyFrom(const BOARD_ITEM *aOther) override
void RenameVariant(const wxString &aOldName, const wxString &aNewName)
Rename a variant.
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
void SetBoardOnly(bool aIsBoardOnly=true)
Definition footprint.h:912
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition footprint.h:482
TRANSFORM_TRS m_transform
Definition footprint.h:1386
timestamp_t m_lastEditTime
Definition footprint.h:1449
PAD * GetPad(const VECTOR2I &aPosition, const LSET &aLayerMask=LSET::AllLayersMask())
Get a pad at aPosition on aLayerMask in the footprint.
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
std::map< const BOARD_ITEM *, std::set< int > > m_netTieCache
Definition footprint.h:1418
std::vector< PROPERTY_BASE * > GetDynamicProperties() const override
Return dynamically-computed properties specific to this object instance (e.g.
wxString m_sheetname
Definition footprint.h:1446
LSET m_stackupLayers
Definition footprint.h:1439
int m_fileFormatVersionAtLoad
Definition footprint.h:1391
void SetLocalClearance(std::optional< int > aClearance)
Definition footprint.h:479
void SetPrivateLayers(const LSET &aLayers)
Definition footprint.h:296
std::optional< int > GetLocalSolderMaskMargin() const
Definition footprint.h:481
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition footprint.h:485
wxString GetKeywords() const
Definition footprint.h:452
bool operator==(const BOARD_ITEM &aOther) const override
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition footprint.h:1280
std::deque< PCB_3D_BODY * > & Bodies()
Definition footprint.h:422
FOOTPRINT_STACKUP GetStackupMode() const
Definition footprint.h:500
virtual void swapData(BOARD_ITEM *aImage) override
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
double GetScaleY() const
Definition footprint.h:400
bool IsPlaced() const
Definition footprint.h:656
bool HasVariant(const wxString &aVariantName) const
Check if a variant exists.
VECTOR2I GetPosition() const override
Definition footprint.h:379
DRAWINGS & GraphicalItems()
Definition footprint.h:358
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
std::vector< SEARCH_TERM > m_searchTerms
Definition footprint.h:1468
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
wxString m_Filename
The 3D shape filename in 3D library.
Definition footprint.h:136
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
const COLLECTORS_GUIDE * GetGuide() const
Definition collectors.h:289
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:39
wxAny Get(PROPERTY_BASE *aProperty) const
Represents an (ordered) list of JUMPER_GROUP objects.
void Add(JUMPER_GROUP aGroup)
Add a JUMPER_GROUP to the set.
Represents a group of jumper pins or pads, keyed by name.
A typed sum value used to carry the before/after of any single property.
wxString ToDisplayString() const
Human-readable representation with no unit context.
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
virtual bool IsOutline() const
Definition font.h:102
Implement outline font drawing.
EMBEDDING_PERMISSION GetEmbeddingPermission() const
virtual wxString GetClass() const =0
Return the class name.
static constexpr double LOD_HIDE
Return this constant from ViewGetLOD() to hide the item unconditionally.
Definition view_item.h:176
static constexpr double LOD_SHOW
Return this constant from ViewGetLOD() to show the item unconditionally.
Definition view_item.h:181
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition view.h:457
Definition kiid.h:46
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & SideSpecificMask()
Definition lset.cpp:732
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static const LSET & AllLayersMask()
Definition lset.cpp:637
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition netinfo.h:285
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::...
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
@ CUSTOM
Shapes can be defined on arbitrary layers.
Definition padstack.h:172
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
Definition padstack.h:171
MODE Mode() const
Definition padstack.h:346
Definition pad.h:61
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:557
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
const wxString & GetNumber() const
Definition pad.h:143
VECTOR2I GetDrillSize() const
Definition pad.h:320
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:205
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:2067
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:297
const PADSTACK & Padstack() const
Definition pad.h:331
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1795
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:434
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:1239
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
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:2129
A 3D body attached to a footprint (or, later, placed on a board).
Definition pcb_3d_body.h:93
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
A geometric constraint between board items (issue #2329).
Abstract dimension API.
wxString GetUntranslatedName() const
Get the untranslated field name for storage, variable look-up, etc.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition pcb_field.cpp:78
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
PCB_FIELD * CloneField() const
Same as Clone, but returns a PCB_FIELD item.
Definition pcb_field.h:102
size_t OwnerHash() const override
Return type-id of the Owner class.
Definition footprint.cpp:95
size_t TypeHash() const override
Return type-id of the property type.
Definition footprint.cpp:97
void setter(void *obj, wxAny &v) override
Definition footprint.cpp:99
size_t BaseHash() const override
Return type-id of the Base class.
Definition footprint.cpp:96
PCB_FOOTPRINT_FIELD_PROPERTY(const wxString &aName)
Definition footprint.cpp:88
wxAny getter(const void *obj) const override
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
Object to handle a bitmap image that can be inserted in a PCB.
VECTOR2I GetLibraryBezierC1() const
int GetWidth() const override
void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, KIGFX::RENDER_SETTINGS *aRenderSettings=nullptr) const override
Convert the item shape to a polyset.
VECTOR2I GetLibraryEnd() const
Definition pcb_shape.h:233
VECTOR2I GetLibraryStart() const
Definition pcb_shape.h:232
STROKE_PARAMS GetStroke() const override
SHAPE_POLY_SET GetLibraryPolyShape() const
VECTOR2I GetLibraryBezierC2() const
VECTOR2I GetLibraryArcMid() const
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:70
bool IsBorderEnabled() const
Tests whether the border is disabled, as configured by the stroke.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Convert the text to a polygonSet describing the actual character strokes (one per segment).
EDA_ANGLE GetTextAngle() const override
Definition pcb_text.cpp:560
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition pcb_text.cpp:411
int GetTextThickness() const override
Definition pcb_text.cpp:497
void SetTextAngle(const EDA_ANGLE &aAngle) override
Definition pcb_text.cpp:572
VECTOR2I GetTextSize() const override
Definition pcb_text.cpp:470
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:275
PROPERTY_BASE(const wxString &aName, PROPERTY_DISPLAY aDisplay=PT_DEFAULT, ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType=ORIGIN_TRANSFORMS::NOT_A_COORD)
Used to generate unique IDs. Must come up front so it's initialized before ctor.
Definition property.h:201
PROPERTY_BASE & SetGroup(const wxString &aGroup)
Definition property.h:378
PROPERTY_BASE & SetIsCopyable(bool aIsCopyable=true)
Definition property.h:371
virtual void SetChoices(const wxPGChoices &aChoices)
Set the possible values for for the property.
Definition property.h:250
PROPERTY_BASE & SetIsHiddenFromLibraryEditors(bool aIsHidden=true)
Definition property.h:351
Provide class metadata.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
bool IsAvailableFor(TYPE_ID aItemClass, PROPERTY_BASE *aProp, INSPECTABLE *aItem)
Checks overriden availability and original availability of a property, returns false if the 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...
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
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.
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)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
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.
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.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
int GetWidth() const
double GetScaleX() const
double GetScaleY() const
const VECTOR2I & GetTranslate() const
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
Definition zone.cpp:657
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
SHAPE_POLY_SET GetLibraryOutline() const
Definition zone.cpp:945
unsigned GetAssignedPriority() const
Definition zone.h:122
@ INTERNAL
Definition common.h:96
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.
@ CHAMFER_ACUTE_CORNERS
Acute angles are chamfered.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
RECURSE_MODE
Definition eda_item.h:50
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
INSPECT_RESULT
Definition eda_item.h:44
const INSPECTOR_FUNC & INSPECTOR
std::function passed to nested users by ref, avoids copying std::function.
Definition eda_item.h:91
#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
@ ELLIPSE
Definition eda_shape.h:62
@ ELLIPSE_ARC
Definition eda_shape.h:63
static struct FOOTPRINT_DESC _FOOTPRINT_DESC
static constexpr std::optional< bool > cmp_points_opt(const VECTOR2I &aPtA, const VECTOR2I &aPtB)
Compare two points, returning std::nullopt if they are identical.
FOOTPRINT_TYPE
Definition footprint.h:98
@ FP_SMD
Definition footprint.h:87
@ FP_THROUGH_HOLE
Definition footprint.h:86
FOOTPRINT_STACKUP
Definition footprint.h:106
@ EXPAND_INNER_LAYERS
The 'normal' stackup handling, where there is a single inner layer (In1) and rule areas using it expa...
Definition footprint.h:111
@ CUSTOM_LAYERS
Stackup handling where the footprint can have any number of copper layers, and objects on those layer...
Definition footprint.h:116
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:41
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:40
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
a few functions useful in geometry calculations.
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition api_utils.cpp:33
Some functions to handle hotkeys in KiCad.
KIID niluuid(0)
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition layer_ids.h:180
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:867
@ LAYER_CONFLICTS_SHADOW
Shadow layer for items flagged conflicting.
Definition layer_ids.h:306
@ LAYER_FOOTPRINTS_FR
Show footprints on front.
Definition layer_ids.h:255
@ LAYER_FP_REFERENCES
Show footprints references (when texts are visible).
Definition layer_ids.h:262
@ LAYER_FP_TEXT
Definition layer_ids.h:236
@ LAYER_FOOTPRINTS_BK
Show footprints on back.
Definition layer_ids.h:256
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:244
@ LAYER_FP_VALUES
Show footprints values (when texts are visible).
Definition layer_ids.h:259
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ Eco1_User
Definition layer_ids.h:105
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ Eco2_User
Definition layer_ids.h:106
@ In1_Cu
Definition layer_ids.h:62
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
bool IsValidLayer(int aLayerId)
Test whether a given integer is a valid layer index, i.e.
Definition layer_ids.h:719
const wxString & GetLibFilenameForbiddenChars()
Characters illegal in a footprint library filename.
Definition lib_id.cpp:40
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:79
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:41
FLIP_DIRECTION
Definition mirror.h:23
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
DIFF_VALUE WxAnyToDiffValue(const wxAny &aValue, PROPERTY_BASE *aProperty)
Convert a wxAny value read from a PROPERTY_BASE getter into a DIFF_VALUE that the engine can store,...
bool BoxHitTest(const VECTOR2I &aHitPoint, const BOX2I &aHittee, int aAccuracy)
Perform a point-to-box hit test.
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:96
void PackEmbeddedFiles(common::types::EmbeddedFiles &aOutput, const EMBEDDED_FILES &aFiles)
bool UnpackEmbeddedFiles(EMBEDDED_FILES &aOutput, const common::types::EmbeddedFiles &aProto)
KICOMMON_API int UnpackDistance(const types::Distance &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API KIID_PATH UnpackSheetPath(const types::SheetPath &aInput)
KICOMMON_API std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition api_utils.cpp:50
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
KICOMMON_API void PackCustomProperties(google::protobuf::RepeatedPtrField< types::CustomProperty > *aOutput, const EDA_ITEM &aItem)
KICOMMON_API void PackDistance(types::Distance &aOutput, int aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackSheetPath(types::SheetPath &aOutput, const KIID_PATH &aInput)
KICOMMON_API void PackLibId(types::LibraryIdentifier *aOutput, const LIB_ID &aId)
KICOMMON_API LIB_ID UnpackLibId(const types::LibraryIdentifier &aId)
KICOMMON_API void UnpackCustomProperties(const google::protobuf::RepeatedPtrField< types::CustomProperty > &aInput, EDA_ITEM &aItem)
KICOMMON_API void PackVector3D(types::Vector3D &aOutput, const VECTOR3D &aInput)
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ PTH
Plated through hole pad.
Definition padstack.h:97
@ FIDUCIAL_LOCAL
a fiducial (usually a smd) local to the parent footprint
Definition padstack.h:117
@ FIDUCIAL_GLBL
a fiducial (usually a smd) for the full board
Definition padstack.h:116
@ MECHANICAL
a pad used for mechanical support
Definition padstack.h:121
@ PRESSFIT
a PTH with a hole diameter with tight tolerances for press fit pin
Definition padstack.h:122
@ HEATSINK
a pad used as heat sink, usually in SMD footprints
Definition padstack.h:119
@ NONE
no special fabrication property
Definition padstack.h:114
@ TESTPOINT
a test point pad
Definition padstack.h:118
@ CASTELLATED
a pad with a castellated through hole
Definition padstack.h:120
@ BGA
Smd pad, used in BGA footprints.
Definition padstack.h:115
#define _HKI(x)
Definition page_info.cpp:40
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
Definition property.h:74
#define NO_SETTER(owner, type)
Macro to define read-only fields (no setter method available)
Definition property.h:904
#define ENUM_TO_WXANY(type)
Definition property.h:899
@ PT_DEGREE
Angle expressed in degrees.
Definition property.h:66
@ PT_RATIO
Definition property.h:68
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
#define REGISTER_TYPE(x)
Helper macro to map type hashes to names.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition ptree.cpp:194
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.
wxString GetDefaultVariantName()
int GetTrailingInt(const wxString &aStr)
Gets the trailing int, if any, from a string.
wxString UnescapeString(const wxString &aSource)
std::vector< wxString > m_pins
Definition footprint.h:905
bool operator()(const BOARD_ITEM *itemA, const BOARD_ITEM *itemB) const
bool operator()(const PAD *aFirst, const PAD *aSecond) const
bool operator()(const ZONE *aFirst, const ZONE *aSecond) const
A structure for storing weighted search terms.
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ USER
The field ID hasn't been set yet; field is invalid.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ UNTRANSLATED
KIBIS_MODEL * model
int clearance
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:227
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition typeinfo.h:260
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_CONSTRAINT_T
a geometric constraint between board items
Definition typeinfo.h:237
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_3D_BODY_T
class PCB_3D_BODY, a 3D body (extruded or model) on a footprint
Definition typeinfo.h:241
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:91
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:87
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:92
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
ZONE_CONNECTION
How pads are covered by copper in zone.
Definition zones.h:43
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ THT_THERMAL
Thermal relief only for THT pads.
Definition zones.h:48
@ NONE
Pads are not covered.
Definition zones.h:45
@ FULL
pads are covered by copper
Definition zones.h:47