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