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