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