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