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