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_POINT_T:
1563 for( auto it = m_points.begin(); it != m_points.end(); ++it )
1564 {
1565 if( *it == static_cast<PCB_POINT*>( aBoardItem ) )
1566 {
1567 m_points.erase( it );
1568 break;
1569 }
1570 }
1571
1572 break;
1573
1574 default:
1575 {
1576 wxString msg;
1577 msg.Printf( wxT( "FOOTPRINT::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
1578 aBoardItem->Type() );
1579 wxFAIL_MSG( msg );
1580 }
1581 }
1582
1583 // If this footprint is on a board, update the board's item-by-id cache
1584 if( BOARD* board = GetBoard(); board && board->IsItemIndexedById( this ) )
1585 board->UncacheItemSubtreeById( aBoardItem );
1586
1587 aBoardItem->SetFlags( STRUCT_DELETED );
1588
1590}
1591
1592
1593double FOOTPRINT::GetArea( int aPadding ) const
1594{
1595 BOX2I bbox = GetBoundingBox( false );
1596
1597 double w = std::abs( static_cast<double>( bbox.GetWidth() ) ) + aPadding;
1598 double h = std::abs( static_cast<double>( bbox.GetHeight() ) ) + aPadding;
1599 return w * h;
1600}
1601
1602
1604{
1605 int smd_count = 0;
1606 int tht_count = 0;
1607
1608 for( PAD* pad : m_pads )
1609 {
1610 switch( pad->GetProperty() )
1611 {
1614 continue;
1615
1616 case PAD_PROP::HEATSINK:
1619 continue;
1620
1621 case PAD_PROP::NONE:
1622 case PAD_PROP::BGA:
1624 case PAD_PROP::PRESSFIT:
1625 break;
1626 }
1627
1628 switch( pad->GetAttribute() )
1629 {
1630 case PAD_ATTRIB::PTH:
1631 tht_count++;
1632 break;
1633
1634 case PAD_ATTRIB::SMD:
1635 if( pad->IsOnCopperLayer() )
1636 smd_count++;
1637
1638 break;
1639
1640 default:
1641 break;
1642 }
1643 }
1644
1645 // Footprints with plated through-hole pads should usually be marked through hole even if they
1646 // also have SMD because they might not be auto-placed. Exceptions to this might be shielded
1647 if( tht_count > 0 )
1648 return FP_THROUGH_HOLE;
1649
1650 if( smd_count > 0 )
1651 return FP_SMD;
1652
1653 return 0;
1654}
1655
1656
1658{
1659 if( ( m_attributes & FP_SMD ) == FP_SMD )
1660 return _( "SMD" );
1661
1663 return _( "Through hole" );
1664
1665 return _( "Other" );
1666}
1667
1668
1669std::vector<SEARCH_TERM>& FOOTPRINT::GetSearchTerms()
1670{
1671 m_searchTerms.clear();
1672 m_searchTerms.reserve( 6 );
1673
1674 m_searchTerms.emplace_back( SEARCH_TERM( GetLibNickname(), 4 ) );
1675 m_searchTerms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
1676 m_searchTerms.emplace_back( SEARCH_TERM( GetLIB_ID().Format(), 16 ) );
1677
1678 wxStringTokenizer keywordTokenizer( GetKeywords(), wxS( " \t\r\n" ), wxTOKEN_STRTOK );
1679
1680 while( keywordTokenizer.HasMoreTokens() )
1681 m_searchTerms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
1682
1683 m_searchTerms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
1684 m_searchTerms.emplace_back( SEARCH_TERM( GetLibDescription(), 1 ) );
1685
1686 return m_searchTerms;
1687}
1688
1689
1691{
1692 BOX2I bbox;
1693
1694 // We want the bounding box of the footprint pads at rot 0, not flipped
1695 // Create such a image:
1696 FOOTPRINT dummy( *this );
1697
1698 dummy.SetPosition( VECTOR2I( 0, 0 ) );
1699 dummy.SetOrientation( ANGLE_0 );
1700
1701 if( dummy.IsFlipped() )
1702 dummy.Flip( VECTOR2I( 0, 0 ), FLIP_DIRECTION::TOP_BOTTOM );
1703
1704 for( PAD* pad : dummy.Pads() )
1705 bbox.Merge( pad->GetBoundingBox() );
1706
1707 return bbox;
1708}
1709
1710
1712{
1713 for( BOARD_ITEM* item : m_drawings )
1714 {
1715 if( m_privateLayers.test( item->GetLayer() ) )
1716 continue;
1717
1718 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_TEXT_T )
1719 return false;
1720 }
1721
1722 return true;
1723}
1724
1725
1727{
1728 return GetBoundingBox( true );
1729}
1730
1731
1732const BOX2I FOOTPRINT::GetBoundingBox( bool aIncludeText ) const
1733{
1734 const BOARD* board = GetBoard();
1735
1736 {
1737 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
1738
1739 if( board )
1740 {
1741 if( !m_geometry_cache )
1742 m_geometry_cache = std::make_unique<FOOTPRINT_GEOMETRY_CACHE_DATA>();
1743
1744 if( aIncludeText )
1745 {
1746 if( m_geometry_cache->bounding_box_timestamp >= board->GetTimeStamp() )
1747 return m_geometry_cache->bounding_box;
1748 }
1749 else
1750 {
1751 if( m_geometry_cache->text_excluded_bbox_timestamp >= board->GetTimeStamp() )
1752 return m_geometry_cache->text_excluded_bbox;
1753 }
1754 }
1755 }
1756
1757 std::vector<PCB_TEXT*> texts;
1758 bool isFPEdit = board && board->IsFootprintHolder();
1759
1760 BOX2I bbox( m_pos );
1761 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Give a min size to the bbox
1762
1763 // Calculate the footprint side
1764 PCB_LAYER_ID footprintSide = GetSide();
1765
1766 for( BOARD_ITEM* item : m_drawings )
1767 {
1768 if( IsValidLayer( item->GetLayer() ) && m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
1769 continue;
1770
1771 // We want the bitmap bounding box just in the footprint editor
1772 // so it will start with the correct initial zoom
1773 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
1774 continue;
1775
1776 // Handle text separately
1777 if( item->Type() == PCB_TEXT_T )
1778 {
1779 texts.push_back( static_cast<PCB_TEXT*>( item ) );
1780 continue;
1781 }
1782
1783 // If we're not including text then drop annotations as well -- unless, of course, it's
1784 // an unsided footprint -- in which case it's likely to be nothing *but* annotations.
1785 if( !aIncludeText && footprintSide != UNDEFINED_LAYER )
1786 {
1787 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
1788 continue;
1789
1790 if( item->GetLayer() == Cmts_User || item->GetLayer() == Dwgs_User
1791 || item->GetLayer() == Eco1_User || item->GetLayer() == Eco2_User )
1792 {
1793 continue;
1794 }
1795 }
1796
1797 bbox.Merge( item->GetBoundingBox() );
1798 }
1799
1800 for( PCB_FIELD* field : m_fields )
1801 {
1802 // Reference and value get their own processing
1803 if( field->IsReference() || field->IsValue() )
1804 continue;
1805
1806 texts.push_back( field );
1807 }
1808
1809 for( PAD* pad : m_pads )
1810 bbox.Merge( pad->GetBoundingBox() );
1811
1812 for( ZONE* zone : m_zones )
1813 bbox.Merge( zone->GetBoundingBox() );
1814
1815 for( PCB_POINT* point : m_points )
1816 bbox.Merge( point->GetBoundingBox() );
1817
1818 bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_zones.empty() );
1819
1820 // Groups do not contribute to the rect, only their members
1821 if( aIncludeText || noDrawItems )
1822 {
1823 // Only PCB_TEXT and PCB_FIELD items are independently selectable; PCB_TEXTBOX items go
1824 // in with other graphic items above.
1825 for( PCB_TEXT* text : texts )
1826 {
1827 if( !isFPEdit && m_privateLayers.test( text->GetLayer() ) )
1828 continue;
1829
1830 if( text->Type() == PCB_FIELD_T && !text->IsVisible() )
1831 continue;
1832
1833 bbox.Merge( text->GetBoundingBox() );
1834 }
1835
1836 // This can be further optimized when aIncludeInvisibleText is true, but currently
1837 // leaving this as is until it's determined there is a noticeable speed hit.
1838 bool valueLayerIsVisible = true;
1839 bool refLayerIsVisible = true;
1840
1841 if( board )
1842 {
1843 // The first "&&" conditional handles the user turning layers off as well as layers
1844 // not being present in the current PCB stackup. Values, references, and all
1845 // footprint text can also be turned off via the GAL meta-layers, so the 2nd and
1846 // 3rd "&&" conditionals handle that.
1847 valueLayerIsVisible = board->IsLayerVisible( Value().GetLayer() )
1848 && board->IsElementVisible( LAYER_FP_VALUES )
1849 && board->IsElementVisible( LAYER_FP_TEXT );
1850
1851 refLayerIsVisible = board->IsLayerVisible( Reference().GetLayer() )
1852 && board->IsElementVisible( LAYER_FP_REFERENCES )
1853 && board->IsElementVisible( LAYER_FP_TEXT );
1854 }
1855
1856
1857 if( ( Value().IsVisible() && valueLayerIsVisible ) || noDrawItems )
1858 {
1859 bbox.Merge( Value().GetBoundingBox() );
1860 }
1861
1862 if( ( Reference().IsVisible() && refLayerIsVisible ) || noDrawItems )
1863 {
1864 bbox.Merge( Reference().GetBoundingBox() );
1865 }
1866 }
1867
1868 if( board )
1869 {
1870 std::lock_guard<std::mutex> lock( m_geometry_cache_mutex );
1871
1872 if( !m_geometry_cache )
1873 m_geometry_cache = std::make_unique<FOOTPRINT_GEOMETRY_CACHE_DATA>();
1874
1875 if( aIncludeText || noDrawItems )
1876 {
1877 m_geometry_cache->bounding_box_timestamp = board->GetTimeStamp();
1878 m_geometry_cache->bounding_box = bbox;
1879 }
1880 else
1881 {
1882 m_geometry_cache->text_excluded_bbox_timestamp = board->GetTimeStamp();
1883 m_geometry_cache->text_excluded_bbox = bbox;
1884 }
1885 }
1886
1887 return bbox;
1888}
1889
1890
1891const BOX2I FOOTPRINT::GetLayerBoundingBox( const LSET& aLayers ) const
1892{
1893 std::vector<PCB_TEXT*> texts;
1894 const BOARD* board = GetBoard();
1895 bool isFPEdit = board && board->IsFootprintHolder();
1896
1897 // Start with an uninitialized bounding box
1898 BOX2I bbox;
1899
1900 for( BOARD_ITEM* item : m_drawings )
1901 {
1902 if( IsValidLayer( item->GetLayer() ) && m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
1903 continue;
1904
1905 if( ( aLayers & item->GetLayerSet() ).none() )
1906 continue;
1907
1908 // We want the bitmap bounding box just in the footprint editor
1909 // so it will start with the correct initial zoom
1910 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
1911 continue;
1912
1913 bbox.Merge( item->GetBoundingBox() );
1914 }
1915
1916 for( PAD* pad : m_pads )
1917 {
1918 if( ( aLayers & pad->GetLayerSet() ).none() )
1919 continue;
1920
1921 bbox.Merge( pad->GetBoundingBox() );
1922 }
1923
1924 for( ZONE* zone : m_zones )
1925 {
1926 if( ( aLayers & zone->GetLayerSet() ).none() )
1927 continue;
1928
1929 bbox.Merge( zone->GetBoundingBox() );
1930 }
1931
1932 for( PCB_POINT* point : m_points )
1933 {
1934 if( m_privateLayers.test( point->GetLayer() ) && !isFPEdit )
1935 continue;
1936
1937 if( ( aLayers & point->GetLayerSet() ).none() )
1938 continue;
1939
1940 bbox.Merge( point->GetBoundingBox() );
1941 }
1942
1943 return bbox;
1944}
1945
1946
1948{
1949 const BOARD* board = GetBoard();
1950 bool isFPEdit = board && board->IsFootprintHolder();
1951
1952 if( board )
1953 {
1954 if( m_geometry_cache && m_geometry_cache->hull_timestamp >= board->GetTimeStamp() )
1955 return m_geometry_cache->hull;
1956 }
1957
1958 SHAPE_POLY_SET rawPolys;
1959
1960 for( BOARD_ITEM* item : m_drawings )
1961 {
1962 if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
1963 continue;
1964
1965 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_REFERENCE_IMAGE_T )
1966 {
1967 item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
1968 ERROR_OUTSIDE );
1969 }
1970
1971 // We intentionally exclude footprint fields from the bounding hull.
1972 }
1973
1974 for( PAD* pad : m_pads )
1975 {
1976 pad->Padstack().ForEachUniqueLayer(
1977 [&]( PCB_LAYER_ID aLayer )
1978 {
1979 pad->TransformShapeToPolygon( rawPolys, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
1980 } );
1981
1982 // In case hole is larger than pad
1983 pad->TransformHoleToPolygon( rawPolys, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
1984 }
1985
1986 for( ZONE* zone : m_zones )
1987 {
1988 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
1989 {
1990 const SHAPE_POLY_SET& layerPoly = *zone->GetFilledPolysList( layer );
1991
1992 for( int ii = 0; ii < layerPoly.OutlineCount(); ii++ )
1993 {
1994 const SHAPE_LINE_CHAIN& poly = layerPoly.COutline( ii );
1995 rawPolys.AddOutline( poly );
1996 }
1997 }
1998 }
1999
2000 // If there are some graphic items, build the actual hull.
2001 // However if no items, create a minimal polygon (can happen if a footprint
2002 // is created with no item: it contains only 2 texts.
2003 if( rawPolys.OutlineCount() == 0 || rawPolys.FullPointCount() < 3 )
2004 {
2005 // generate a small dummy rectangular outline around the anchor
2006 const int halfsize = pcbIUScale.mmToIU( 1.0 );
2007
2008 rawPolys.NewOutline();
2009
2010 // add a square:
2011 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
2012 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
2013 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y + halfsize );
2014 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y + halfsize );
2015 }
2016
2017 std::vector<VECTOR2I> convex_hull;
2018 BuildConvexHull( convex_hull, rawPolys );
2019
2020 if( !m_geometry_cache )
2021 m_geometry_cache = std::make_unique<FOOTPRINT_GEOMETRY_CACHE_DATA>();
2022
2023 m_geometry_cache->hull.RemoveAllContours();
2024 m_geometry_cache->hull.NewOutline();
2025
2026 for( const VECTOR2I& pt : convex_hull )
2027 m_geometry_cache->hull.Append( pt );
2028
2029 if( board )
2030 m_geometry_cache->hull_timestamp = board->GetTimeStamp();
2031
2032 return m_geometry_cache->hull;
2033}
2034
2035
2037{
2038 const BOARD* board = GetBoard();
2039 bool isFPEdit = board && board->IsFootprintHolder();
2040
2041 SHAPE_POLY_SET rawPolys;
2042 SHAPE_POLY_SET hull;
2043
2044 for( BOARD_ITEM* item : m_drawings )
2045 {
2046 if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
2047 continue;
2048
2049 if( item->IsOnLayer( aLayer ) )
2050 {
2051 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_REFERENCE_IMAGE_T )
2052 {
2053 item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
2054 ERROR_OUTSIDE );
2055 }
2056
2057 // We intentionally exclude footprint fields from the bounding hull.
2058 }
2059 }
2060
2061 for( PAD* pad : m_pads )
2062 {
2063 if( pad->IsOnLayer( aLayer ) )
2064 pad->TransformShapeToPolygon( rawPolys, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2065 }
2066
2067 for( ZONE* zone : m_zones )
2068 {
2069 if( zone->GetIsRuleArea() )
2070 continue;
2071
2072 if( zone->IsOnLayer( aLayer ) )
2073 {
2074 const std::shared_ptr<SHAPE_POLY_SET>& layerPoly = zone->GetFilledPolysList( aLayer );
2075
2076 for( int ii = 0; ii < layerPoly->OutlineCount(); ii++ )
2077 rawPolys.AddOutline( layerPoly->COutline( ii ) );
2078 }
2079 }
2080
2081 std::vector<VECTOR2I> convex_hull;
2082 BuildConvexHull( convex_hull, rawPolys );
2083
2084 hull.NewOutline();
2085
2086 for( const VECTOR2I& pt : convex_hull )
2087 hull.Append( pt );
2088
2089 return hull;
2090}
2091
2092
2093void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
2094{
2095 wxString msg, msg2;
2096 wxString variant;
2097
2098 if( BOARD* board = GetBoard() )
2099 variant = board->GetCurrentVariant();
2100
2101 // Don't use GetShownText(); we want to see the variable references here
2102 aList.emplace_back( UnescapeString( Reference().GetText() ),
2104
2105 if( aFrame->IsType( FRAME_FOOTPRINT_VIEWER )
2106 || aFrame->IsType( FRAME_FOOTPRINT_CHOOSER )
2107 || aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
2108 {
2109 size_t padCount = GetPadCount( DO_NOT_INCLUDE_NPTH );
2110
2111 aList.emplace_back( _( "Library" ), GetFPID().GetLibNickname().wx_str() );
2112
2113 aList.emplace_back( _( "Footprint Name" ), GetFPID().GetLibItemName().wx_str() );
2114
2115 aList.emplace_back( _( "Pads" ), wxString::Format( wxT( "%zu" ), padCount ) );
2116
2117 aList.emplace_back( wxString::Format( _( "Doc: %s" ), GetLibDescription() ),
2118 wxString::Format( _( "Keywords: %s" ), GetKeywords() ) );
2119
2120 return;
2121 }
2122
2123 // aFrame is the board editor:
2124
2125 switch( GetSide() )
2126 {
2127 case F_Cu: aList.emplace_back( _( "Board Side" ), _( "Front" ) ); break;
2128 case B_Cu: aList.emplace_back( _( "Board Side" ), _( "Back (Flipped)" ) ); break;
2129 default: /* unsided: user-layers only, etc. */ break;
2130 }
2131
2132 aList.emplace_back( _( "Rotation" ), wxString::Format( wxT( "%.4g" ), GetOrientation().AsDegrees() ) );
2133
2134 auto addToken = []( wxString* aStr, const wxString& aAttr )
2135 {
2136 if( !aStr->IsEmpty() )
2137 *aStr += wxT( ", " );
2138
2139 *aStr += aAttr;
2140 };
2141
2142 wxString status;
2143 wxString attrs;
2144
2145 if( IsLocked() )
2146 addToken( &status, _( "Locked" ) );
2147
2148 if( IsPlaced() )
2149 addToken( &status, _( "autoplaced" ) );
2150
2151 if( IsBoardOnly() )
2152 addToken( &attrs, _( "not in schematic" ) );
2153
2154 if( GetExcludedFromPosFilesForVariant( variant ) )
2155 addToken( &attrs, _( "exclude from pos files" ) );
2156
2157 if( GetExcludedFromBOMForVariant( variant ) )
2158 addToken( &attrs, _( "exclude from BOM" ) );
2159
2160 if( GetDNPForVariant( variant ) )
2161 addToken( &attrs, _( "DNP" ) );
2162
2163 aList.emplace_back( _( "Status: " ) + status, _( "Attributes:" ) + wxS( " " ) + attrs );
2164
2165 if( !m_componentClassCacheProxy->GetComponentClass()->IsEmpty() )
2166 {
2167 aList.emplace_back( _( "Component Class" ),
2168 m_componentClassCacheProxy->GetComponentClass()->GetHumanReadableName() );
2169 }
2170
2171 msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
2172 msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" ) : m_3D_Drawings.front().m_Filename );
2173 aList.emplace_back( msg, msg2 );
2174
2175 msg.Printf( _( "Doc: %s" ), m_libDescription );
2176 msg2.Printf( _( "Keywords: %s" ), m_keywords );
2177 aList.emplace_back( msg, msg2 );
2178}
2179
2180
2182{
2183 if( const BOARD* board = GetBoard() )
2184 {
2185 if( board->IsFootprintHolder() )
2186 return UNDEFINED_LAYER;
2187 }
2188
2189 // Test pads first; they're the most likely to return a quick answer.
2190 for( PAD* pad : m_pads )
2191 {
2192 if( ( LSET::SideSpecificMask() & pad->GetLayerSet() ).any() )
2193 return GetLayer();
2194 }
2195
2196 for( BOARD_ITEM* item : m_drawings )
2197 {
2198 if( IsValidLayer( item->GetLayer() ) && LSET::SideSpecificMask().test( item->GetLayer() ) )
2199 return GetLayer();
2200 }
2201
2202 for( ZONE* zone : m_zones )
2203 {
2204 if( ( LSET::SideSpecificMask() & zone->GetLayerSet() ).any() )
2205 return GetLayer();
2206 }
2207
2208 return UNDEFINED_LAYER;
2209}
2210
2211
2213{
2214 // If we have any pads, fall back on normal checking
2215 for( PAD* pad : m_pads )
2216 {
2217 if( pad->IsOnLayer( aLayer ) )
2218 return true;
2219 }
2220
2221 for( ZONE* zone : m_zones )
2222 {
2223 if( zone->IsOnLayer( aLayer ) )
2224 return true;
2225 }
2226
2227 for( PCB_FIELD* field : m_fields )
2228 {
2229 if( field->IsOnLayer( aLayer ) )
2230 return true;
2231 }
2232
2233 for( BOARD_ITEM* item : m_drawings )
2234 {
2235 if( item->IsOnLayer( aLayer ) )
2236 return true;
2237 }
2238
2239 return false;
2240}
2241
2242
2243bool FOOTPRINT::HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy ) const
2244{
2245 for( PAD* pad : m_pads )
2246 {
2247 if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition, aAccuracy ) )
2248 return true;
2249 }
2250
2251 for( ZONE* zone : m_zones )
2252 {
2253 if( zone->IsOnLayer( aLayer ) && zone->HitTest( aPosition, aAccuracy ) )
2254 return true;
2255 }
2256
2257 for( BOARD_ITEM* item : m_drawings )
2258 {
2259 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer )
2260 && item->HitTest( aPosition, aAccuracy ) )
2261 {
2262 return true;
2263 }
2264 }
2265
2266 return false;
2267}
2268
2269
2270bool FOOTPRINT::HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy ) const
2271{
2272 std::vector<BOARD_ITEM*> items;
2273
2274 for( PAD* pad : m_pads )
2275 {
2276 if( pad->IsOnLayer( aLayer ) )
2277 items.push_back( pad );
2278 }
2279
2280 for( ZONE* zone : m_zones )
2281 {
2282 if( zone->IsOnLayer( aLayer ) )
2283 items.push_back( zone );
2284 }
2285
2286 for( BOARD_ITEM* item : m_drawings )
2287 {
2288 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) )
2289 items.push_back( item );
2290 }
2291
2292 // If we require the elements to be contained in the rect and any of them are not,
2293 // we can return false;
2294 // Conversely, if we just require any of the elements to have a hit, we can return true
2295 // when the first one is found.
2296 for( BOARD_ITEM* item : items )
2297 {
2298 if( !aContained && item->HitTest( aRect, aContained, aAccuracy ) )
2299 return true;
2300 else if( aContained && !item->HitTest( aRect, aContained, aAccuracy ) )
2301 return false;
2302 }
2303
2304 // If we didn't exit in the loop, that means that we did not return false for aContained or
2305 // we did not return true for !aContained. So we can just return the bool with a test of
2306 // whether there were any elements or not.
2307 return !items.empty() && aContained;
2308}
2309
2310
2311bool FOOTPRINT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
2312{
2313 BOX2I rect = GetBoundingBox( false );
2314 return rect.Inflate( aAccuracy ).Contains( aPosition );
2315}
2316
2317
2318bool FOOTPRINT::HitTestAccurate( const VECTOR2I& aPosition, int aAccuracy ) const
2319{
2320 return GetBoundingHull().Collide( aPosition, aAccuracy );
2321}
2322
2323
2324bool FOOTPRINT::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
2325{
2326 BOX2I arect = aRect;
2327 arect.Inflate( aAccuracy );
2328
2329 if( aContained )
2330 {
2331 return arect.Contains( GetBoundingBox( false ) );
2332 }
2333 else
2334 {
2335 // If the rect does not intersect the bounding box, skip any tests
2336 if( !aRect.Intersects( GetBoundingBox( false ) ) )
2337 return false;
2338
2339 // If there are no pads, zones, or drawings, allow intersection with text
2340 if( m_pads.empty() && m_zones.empty() && m_drawings.empty() )
2341 return GetBoundingBox( true ).Intersects( arect );
2342
2343 // Determine if any elements in the FOOTPRINT intersect the rect
2344 for( PAD* pad : m_pads )
2345 {
2346 if( pad->HitTest( arect, false, 0 ) )
2347 return true;
2348 }
2349
2350 for( ZONE* zone : m_zones )
2351 {
2352 if( zone->HitTest( arect, false, 0 ) )
2353 return true;
2354 }
2355
2356 for( PCB_POINT* point : m_points )
2357 {
2358 if( point->HitTest( arect, false, 0 ) )
2359 return true;
2360 }
2361
2362 // PCB fields are selectable on their own, so they don't get tested
2363
2364 for( BOARD_ITEM* item : m_drawings )
2365 {
2366 // Text items are selectable on their own, and are therefore excluded from this
2367 // test. TextBox items are NOT selectable on their own, and so MUST be included
2368 // here. Bitmaps aren't selectable since they aren't displayed.
2369 if( item->Type() != PCB_TEXT_T && item->HitTest( arect, false, 0 ) )
2370 return true;
2371 }
2372
2373 // Groups are not hit-tested; only their members
2374
2375 // No items were hit
2376 return false;
2377 }
2378}
2379
2380
2381bool FOOTPRINT::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
2382{
2383 using std::ranges::all_of;
2384 using std::ranges::any_of;
2385
2386 // If there are no pads, zones, or drawings, test footprint text instead.
2387 if( m_pads.empty() && m_zones.empty() && m_drawings.empty() )
2388 return KIGEOM::BoxHitTest( aPoly, GetBoundingBox( true ), aContained );
2389
2390 auto hitTest =
2391 [&]( const auto* aItem )
2392 {
2393 return aItem && aItem->HitTest( aPoly, aContained );
2394 };
2395
2396 // Filter out text items from the drawings, since they are selectable on their own,
2397 // and we don't want to select the whole footprint when text is hit. TextBox items are NOT
2398 // selectable on their own, so they are not excluded here.
2399 auto drawings = m_drawings | std::views::filter( []( const auto* aItem )
2400 {
2401 return aItem && aItem->Type() != PCB_TEXT_T;
2402 } );
2403
2404 // Test pads, zones and drawings with text excluded. PCB fields are also selectable
2405 // on their own, so they don't get tested. Groups are not hit-tested, only their members.
2406 // Bitmaps aren't selectable since they aren't displayed.
2407 if( aContained )
2408 {
2409 // All items must be contained in the selection poly.
2410 return all_of( drawings, hitTest )
2411 && all_of( m_pads, hitTest )
2412 && all_of( m_zones, hitTest );
2413 }
2414 else
2415 {
2416 // Any item intersecting the selection poly is sufficient.
2417 return any_of( drawings, hitTest )
2418 || any_of( m_pads, hitTest )
2419 || any_of( m_zones, hitTest );
2420 }
2421}
2422
2423
2424PAD* FOOTPRINT::FindPadByNumber( const wxString& aPadNumber, PAD* aSearchAfterMe ) const
2425{
2426 bool can_select = aSearchAfterMe ? false : true;
2427
2428 for( PAD* pad : m_pads )
2429 {
2430 if( !can_select && pad == aSearchAfterMe )
2431 {
2432 can_select = true;
2433 continue;
2434 }
2435
2436 if( can_select && pad->GetNumber() == aPadNumber )
2437 return pad;
2438 }
2439
2440 return nullptr;
2441}
2442
2443
2444PAD* FOOTPRINT::FindPadByUuid( const KIID& aUuid ) const
2445{
2446 for( PAD* pad : m_pads )
2447 {
2448 if( pad->m_Uuid == aUuid )
2449 return pad;
2450 }
2451
2452 return nullptr;
2453}
2454
2455
2456PAD* FOOTPRINT::GetPad( const VECTOR2I& aPosition, const LSET& aLayerMask )
2457{
2458 for( PAD* pad : m_pads )
2459 {
2460 // ... and on the correct layer.
2461 if( !( pad->GetLayerSet() & aLayerMask ).any() )
2462 continue;
2463
2464 if( pad->HitTest( aPosition ) )
2465 return pad;
2466 }
2467
2468 return nullptr;
2469}
2470
2471
2472std::vector<const PAD*> FOOTPRINT::GetPads( const wxString& aPadNumber, const PAD* aIgnore ) const
2473{
2474 std::vector<const PAD*> retv;
2475
2476 for( const PAD* pad : m_pads )
2477 {
2478 if( ( aIgnore && aIgnore == pad ) || ( pad->GetNumber() != aPadNumber ) )
2479 continue;
2480
2481 retv.push_back( pad );
2482 }
2483
2484 return retv;
2485}
2486
2487
2488unsigned FOOTPRINT::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
2489{
2490 if( aIncludeNPTH )
2491 return m_pads.size();
2492
2493 unsigned cnt = 0;
2494
2495 for( PAD* pad : m_pads )
2496 {
2497 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2498 continue;
2499
2500 cnt++;
2501 }
2502
2503 return cnt;
2504}
2505
2506
2507std::set<wxString> FOOTPRINT::GetUniquePadNumbers( INCLUDE_NPTH_T aIncludeNPTH ) const
2508{
2509 std::set<wxString> usedNumbers;
2510
2511 // Create a set of used pad numbers
2512 for( PAD* pad : m_pads )
2513 {
2514 // Skip pads not on copper layers (used to build complex
2515 // solder paste shapes for instance)
2516 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
2517 continue;
2518
2519 // Skip pads with no name, because they are usually "mechanical"
2520 // pads, not "electrical" pads
2521 if( pad->GetNumber().IsEmpty() )
2522 continue;
2523
2524 if( !aIncludeNPTH )
2525 {
2526 // skip NPTH
2527 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2528 continue;
2529 }
2530
2531 usedNumbers.insert( pad->GetNumber() );
2532 }
2533
2534 return usedNumbers;
2535}
2536
2537
2538unsigned FOOTPRINT::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
2539{
2540 return GetUniquePadNumbers( aIncludeNPTH ).size();
2541}
2542
2543
2545{
2546 // A pad number is "electrical" (i.e. maps to a schematic pin) when it is either:
2547 // - purely numeric: "1", "42"
2548 // - BGA / alphanumeric style: up to two leading letters followed by digits, e.g.
2549 // "A1", "B12", "AA3", "AB10"
2550 // Mounting-pad designators such as "MP" do not end in a digit typically
2551 // and are intentionally excluded.
2552 auto isElectricalPadNumber = []( const wxString& num ) -> bool
2553 {
2554 if( num.IsEmpty() )
2555 return false;
2556
2557 // Walk past an optional alphabetic prefix of at most two characters.
2558 size_t i = 0;
2559 while( i < num.size() && wxIsalpha( num[i] ) )
2560 ++i;
2561
2562 // Prefix must be 0–2 letters; anything longer is not a pin number.
2563 if( i > 2 )
2564 return false;
2565
2566 // The remainder must be non-empty and consist entirely of digits.
2567 if( i == num.size() )
2568 return false; // no digits at all (e.g. "MP", "GND")
2569
2570 for( size_t j = i; j < num.size(); ++j )
2571 {
2572 if( !wxIsdigit( num[j] ) )
2573 return false;
2574 }
2575
2576 return true;
2577 };
2578
2579 std::set<wxString> counted;
2580
2581 for( const PAD* pad : m_pads )
2582 {
2583 // Must be on at least one copper layer.
2584 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
2585 continue;
2586
2587 // Skip NPTH (mechanical holes).
2588 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
2589 continue;
2590
2591 const wxString& num = pad->GetNumber();
2592
2593 if( isElectricalPadNumber( num ) )
2594 counted.insert( num );
2595 }
2596
2597 return static_cast<unsigned>( counted.size() );
2598}
2599
2600
2602{
2603 if( nullptr == a3DModel )
2604 return;
2605
2606 if( !a3DModel->m_Filename.empty() )
2607 m_3D_Drawings.push_back( *a3DModel );
2608}
2609
2610
2612{
2613 if( !m_extrudedBody )
2614 m_extrudedBody = std::make_unique<EXTRUDED_3D_BODY>();
2615
2616 return *m_extrudedBody;
2617}
2618
2619
2620void FOOTPRINT::SetExtrudedBody( std::unique_ptr<EXTRUDED_3D_BODY> aBody )
2621{
2622 m_extrudedBody = std::move( aBody );
2623}
2624
2625
2626bool FOOTPRINT::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
2627{
2628 if( aSearchData.searchMetadata )
2629 {
2630 if( EDA_ITEM::Matches( GetFPIDAsString(), aSearchData ) )
2631 return true;
2632
2633 if( EDA_ITEM::Matches( GetLibDescription(), aSearchData ) )
2634 return true;
2635
2636 if( EDA_ITEM::Matches( GetKeywords(), aSearchData ) )
2637 return true;
2638 }
2639
2640 return false;
2641}
2642
2643
2644// see footprint.h
2645INSPECT_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData,
2646 const std::vector<KICAD_T>& aScanTypes )
2647{
2648#if 0 && defined(DEBUG)
2649 std::cout << GetClass().mb_str() << ' ';
2650#endif
2651
2652 bool drawingsScanned = false;
2653
2654 for( KICAD_T scanType : aScanTypes )
2655 {
2656 switch( scanType )
2657 {
2658 case PCB_FOOTPRINT_T:
2659 if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
2660 return INSPECT_RESULT::QUIT;
2661
2662 break;
2663
2664 case PCB_PAD_T:
2665 if( IterateForward<PAD*>( m_pads, inspector, testData, { scanType } )
2667 {
2668 return INSPECT_RESULT::QUIT;
2669 }
2670
2671 break;
2672
2673 case PCB_ZONE_T:
2674 if( IterateForward<ZONE*>( m_zones, inspector, testData, { scanType } )
2676 {
2677 return INSPECT_RESULT::QUIT;
2678 }
2679
2680 break;
2681
2682 case PCB_FIELD_T:
2683 if( IterateForward<PCB_FIELD*>( m_fields, inspector, testData, { scanType } )
2685 {
2686 return INSPECT_RESULT::QUIT;
2687 }
2688
2689 break;
2690
2691 case PCB_TEXT_T:
2692 case PCB_DIM_ALIGNED_T:
2693 case PCB_DIM_LEADER_T:
2694 case PCB_DIM_CENTER_T:
2695 case PCB_DIM_RADIAL_T:
2697 case PCB_SHAPE_T:
2698 case PCB_BARCODE_T:
2699 case PCB_TEXTBOX_T:
2700 case PCB_TABLE_T:
2701 case PCB_TABLECELL_T:
2702 if( !drawingsScanned )
2703 {
2704 if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, aScanTypes )
2706 {
2707 return INSPECT_RESULT::QUIT;
2708 }
2709
2710 drawingsScanned = true;
2711 }
2712
2713 break;
2714
2715 case PCB_GROUP_T:
2716 if( IterateForward<PCB_GROUP*>( m_groups, inspector, testData, { scanType } )
2718 {
2719 return INSPECT_RESULT::QUIT;
2720 }
2721
2722 break;
2723
2724 case PCB_POINT_T:
2725 if( IterateForward<PCB_POINT*>( m_points, inspector, testData, { scanType } )
2727 {
2728 return INSPECT_RESULT::QUIT;
2729 }
2730
2731 break;
2732
2733 default:
2734 break;
2735 }
2736 }
2737
2739}
2740
2741
2742wxString FOOTPRINT::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
2743{
2744 wxString reference = GetReference();
2745
2746 if( reference.IsEmpty() )
2747 reference = _( "<no reference designator>" );
2748
2749 return wxString::Format( _( "Footprint %s" ), reference );
2750}
2751
2752
2753wxString FOOTPRINT::DisambiguateItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
2754{
2755 return wxString::Format( wxT( "%s (%s)" ),
2756 GetItemDescription( aUnitsProvider, aFull ),
2757 GetFPIDAsString() );
2758}
2759
2760
2762{
2763 return BITMAPS::module;
2764}
2765
2766
2768{
2769 return new FOOTPRINT( *this );
2770}
2771
2772
2773void FOOTPRINT::RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFunction, RECURSE_MODE aMode ) const
2774{
2775 try
2776 {
2777 for( PCB_FIELD* field : m_fields )
2778 aFunction( field );
2779
2780 for( PAD* pad : m_pads )
2781 aFunction( pad );
2782
2783 for( ZONE* zone : m_zones )
2784 aFunction( zone );
2785
2786 for( PCB_GROUP* group : m_groups )
2787 aFunction( group );
2788
2789 for( PCB_POINT* point : m_points )
2790 aFunction( point );
2791
2792 for( BOARD_ITEM* drawing : m_drawings )
2793 {
2794 aFunction( drawing );
2795
2796 if( aMode == RECURSE_MODE::RECURSE )
2797 drawing->RunOnChildren( aFunction, RECURSE_MODE::RECURSE );
2798 }
2799 }
2800 catch( std::bad_function_call& )
2801 {
2802 wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnChildren" ) );
2803 }
2804}
2805
2806
2807std::vector<int> FOOTPRINT::ViewGetLayers() const
2808{
2809 std::vector<int> layers;
2810
2811 layers.reserve( 6 );
2812 layers.push_back( LAYER_ANCHOR );
2813
2814 switch( m_layer )
2815 {
2816 default:
2817 wxASSERT_MSG( false, wxT( "Illegal layer" ) ); // do you really have footprints placed
2818 // on other layers?
2820
2821 case F_Cu:
2822 layers.push_back( LAYER_FOOTPRINTS_FR );
2823 break;
2824
2825 case B_Cu:
2826 layers.push_back( LAYER_FOOTPRINTS_BK );
2827 break;
2828 }
2829
2830 layers.push_back( LAYER_CONFLICTS_SHADOW );
2831
2832 // If there are no pads, and only drawings on a silkscreen layer, then report the silkscreen
2833 // layer as well so that the component can be edited with the silkscreen layer
2834 bool f_silk = false, b_silk = false, non_silk = false;
2835
2836 for( BOARD_ITEM* item : m_drawings )
2837 {
2838 if( item->GetLayer() == F_SilkS )
2839 f_silk = true;
2840 else if( item->GetLayer() == B_SilkS )
2841 b_silk = true;
2842 else
2843 non_silk = true;
2844 }
2845
2846 if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
2847 {
2848 if( f_silk )
2849 layers.push_back( F_SilkS );
2850
2851 if( b_silk )
2852 layers.push_back( B_SilkS );
2853 }
2854
2855 return layers;
2856}
2857
2858
2859double FOOTPRINT::ViewGetLOD( int aLayer, const KIGFX::VIEW* aView ) const
2860{
2861 if( aLayer == LAYER_CONFLICTS_SHADOW && IsConflicting() )
2862 {
2863 // The locked shadow shape is shown only if the footprint itself is visible
2864 if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
2865 return LOD_SHOW;
2866
2867 if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
2868 return LOD_SHOW;
2869
2870 return LOD_HIDE;
2871 }
2872
2873 // Only show anchors if the layer the footprint is on is visible
2874 if( aLayer == LAYER_ANCHOR && !aView->IsLayerVisible( m_layer ) )
2875 return LOD_HIDE;
2876
2877 int layer = ( m_layer == F_Cu ) ? LAYER_FOOTPRINTS_FR :
2879
2880 // Currently this is only pertinent for the anchor layer; everything else is drawn from the
2881 // children.
2882 // The "good" value is experimentally chosen.
2883 constexpr double MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY = 1.5;
2884
2885 if( aView->IsLayerVisible( layer ) )
2886 return MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY;
2887
2888 return LOD_HIDE;
2889}
2890
2891
2893{
2894 BOX2I area = GetBoundingBox( true );
2895
2896 // Inflate in case clearance lines are drawn around pads, etc.
2897 if( const BOARD* board = GetBoard() )
2898 {
2899 int biggest_clearance = board->GetMaxClearanceValue();
2900 area.Inflate( biggest_clearance );
2901 }
2902
2903 return area;
2904}
2905
2906
2907bool FOOTPRINT::IsLibNameValid( const wxString & aName )
2908{
2909 const wxChar * invalids = StringLibNameInvalidChars( false );
2910
2911 if( aName.find_first_of( invalids ) != std::string::npos )
2912 return false;
2913
2914 return true;
2915}
2916
2917
2918const wxChar* FOOTPRINT::StringLibNameInvalidChars( bool aUserReadable )
2919{
2920 // This list of characters is also duplicated in validators.cpp and
2921 // lib_id.cpp
2922 // TODO: Unify forbidden character lists - Warning, invalid filename characters are not the same
2923 // as invalid LIB_ID characters. We will need to separate the FP filenames from FP names before this
2924 // can be unified
2925 static const wxChar invalidChars[] = wxT("%$<>\t\n\r\"\\/:");
2926 static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
2927
2928 if( aUserReadable )
2929 return invalidCharsReadable;
2930 else
2931 return invalidChars;
2932}
2933
2934
2935void FOOTPRINT::Move( const VECTOR2I& aMoveVector )
2936{
2937 if( aMoveVector.x == 0 && aMoveVector.y == 0 )
2938 return;
2939
2940 VECTOR2I newpos = m_pos + aMoveVector;
2941 SetPosition( newpos );
2942}
2943
2944
2945void FOOTPRINT::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
2946{
2947 if( aAngle == ANGLE_0 )
2948 return;
2949
2950 EDA_ANGLE orientation = GetOrientation();
2951 EDA_ANGLE newOrientation = orientation + aAngle;
2952 VECTOR2I newpos = m_pos;
2953 RotatePoint( newpos, aRotCentre, aAngle );
2954 SetPosition( newpos );
2955 SetOrientation( newOrientation );
2956
2957 for( PCB_FIELD* field : m_fields )
2958 field->KeepUpright();
2959
2960 for( BOARD_ITEM* item : m_drawings )
2961 {
2962 if( item->Type() == PCB_TEXT_T )
2963 static_cast<PCB_TEXT*>( item )->KeepUpright();
2964 }
2965}
2966
2967
2969{
2970 wxASSERT( aLayer == F_Cu || aLayer == B_Cu );
2971
2972 if( aLayer != GetLayer() )
2974}
2975
2976
2977void FOOTPRINT::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
2978{
2979 // Move footprint to its final position:
2980 VECTOR2I finalPos = m_pos;
2981
2982 // Now Flip the footprint.
2983 // Flipping a footprint is a specific transform: it is not mirrored like a text.
2984 // We have to change the side, and ensure the footprint rotation is modified according to the
2985 // transform, because this parameter is used in pick and place files, and when updating the
2986 // footprint from library.
2987 // When flipped around the X axis (Y coordinates changed) orientation is negated
2988 // When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
2989 // Because it is specific to a footprint, we flip around the X axis, and after rotate 180 deg
2990
2991 MIRROR( finalPos.y, aCentre.y );
2992
2993 SetPosition( finalPos );
2994
2995 // Flip layer
2997
2998 // Calculate the new orientation, and then clear it for pad flipping.
2999 EDA_ANGLE newOrientation = -m_orient;
3000 newOrientation.Normalize180();
3001 m_orient = ANGLE_0;
3002
3003 // Mirror fields to other side of board.
3004 for( PCB_FIELD* field : m_fields )
3005 field->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
3006
3007 // Mirror pads to other side of board.
3008 for( PAD* pad : m_pads )
3010
3011 // Now set the new orientation.
3012 m_orient = newOrientation;
3013
3014 // Mirror zones to other side of board.
3015 for( ZONE* zone : m_zones )
3016 zone->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
3017
3018 // Reverse mirror footprint graphics and texts.
3019 for( BOARD_ITEM* item : m_drawings )
3020 item->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
3021
3022 // Points move but don't flip layer
3023 for( PCB_POINT* point : m_points )
3024 point->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
3025
3026 // Swap the courtyard sides, then mirror in the same way as everything else.
3027 if( m_courtyard_cache )
3028 {
3029 std::swap( m_courtyard_cache->back, m_courtyard_cache->front );
3031 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3032
3034 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3035 }
3036
3037 // Flip the extrusion source layer to match the new side.
3038 if( m_extrudedBody && m_extrudedBody->m_layer != UNDEFINED_LAYER )
3039 m_extrudedBody->m_layer = GetBoard()->FlipLayer( m_extrudedBody->m_layer );
3040
3041 if( m_geometry_cache )
3043
3044 // Now rotate 180 deg if required
3045 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
3046 Rotate( aCentre, ANGLE_180 );
3047
3048 if( m_geometry_cache )
3049 m_geometry_cache->text_excluded_bbox_timestamp = 0;
3050}
3051
3052
3054{
3055 VECTOR2I delta = aPos - m_pos;
3056
3057 m_pos += delta;
3058
3059 for( PCB_FIELD* field : m_fields )
3060 field->EDA_TEXT::Offset( delta );
3061
3062 for( PAD* pad : m_pads )
3063 pad->SetPosition( pad->GetPosition() + delta );
3064
3065 for( ZONE* zone : m_zones )
3066 zone->Move( delta );
3067
3068 for( BOARD_ITEM* item : m_drawings )
3069 item->Move( delta );
3070
3071 for( PCB_POINT* point : m_points )
3072 point->Move( delta );
3073
3074 if( m_geometry_cache )
3075 {
3076 m_geometry_cache->bounding_box.Move( delta );
3077 m_geometry_cache->text_excluded_bbox.Move( delta );
3078 m_geometry_cache->hull.Move( delta );
3079 }
3080
3081 // The geometry work has been conserved by using Move(). But the hashes
3082 // need to be updated, otherwise the cached polygons will still be rebuild.
3083 if( m_courtyard_cache )
3084 {
3085 m_courtyard_cache->back.Move( delta );
3086 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3087 m_courtyard_cache->front.Move( delta );
3088 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3089 }
3090}
3091
3092
3093void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
3094{
3095 /*
3096 * Move the reference point of the footprint
3097 * the footprints elements (pads, outlines, edges .. ) are moved
3098 * but:
3099 * - the footprint position is not modified.
3100 * - the relative (local) coordinates of these items are modified
3101 * - Draw coordinates are updated
3102 */
3103
3104 // Update (move) the relative coordinates relative to the new anchor point.
3105 VECTOR2I moveVector = aMoveVector;
3106 RotatePoint( moveVector, -GetOrientation() );
3107
3108 // Update field local coordinates
3109 for( PCB_FIELD* field : m_fields )
3110 field->Move( moveVector );
3111
3112 // Update the pad local coordinates.
3113 for( PAD* pad : m_pads )
3114 pad->Move( moveVector );
3115
3116 // Update the draw element coordinates.
3117 for( BOARD_ITEM* item : GraphicalItems() )
3118 item->Move( moveVector );
3119
3120 // Update the keepout zones
3121 for( ZONE* zone : Zones() )
3122 zone->Move( moveVector );
3123
3124 // Update the 3D models
3125 for( FP_3DMODEL& model : Models() )
3126 {
3127 model.m_Offset.x += pcbIUScale.IUTomm( moveVector.x );
3128 model.m_Offset.y -= pcbIUScale.IUTomm( moveVector.y );
3129 }
3130
3131 if( m_geometry_cache )
3132 {
3133 m_geometry_cache->bounding_box.Move( moveVector );
3134 m_geometry_cache->text_excluded_bbox.Move( moveVector );
3135 m_geometry_cache->hull.Move( moveVector );
3136 }
3137
3138 // The geometry work have been conserved by using Move(). But the hashes
3139 // need to be updated, otherwise the cached polygons will still be rebuild.
3140 if( m_courtyard_cache )
3141 {
3142 m_courtyard_cache->back.Move( moveVector );
3143 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3144 m_courtyard_cache->front.Move( moveVector );
3145 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3146 }
3147}
3148
3149
3150void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
3151{
3152 EDA_ANGLE angleChange = aNewAngle - m_orient; // change in rotation
3153
3154 m_orient = aNewAngle;
3155 m_orient.Normalize180();
3156
3157 const VECTOR2I rotationCenter = GetPosition();
3158
3159 for( PCB_FIELD* field : m_fields )
3160 field->Rotate( rotationCenter, angleChange );
3161
3162 for( PAD* pad : m_pads )
3163 pad->Rotate( rotationCenter, angleChange );
3164
3165 for( ZONE* zone : m_zones )
3166 zone->Rotate( rotationCenter, angleChange );
3167
3168 for( BOARD_ITEM* item : m_drawings )
3169 item->Rotate( rotationCenter, angleChange );
3170
3171 for( PCB_POINT* point : m_points )
3172 point->Rotate( rotationCenter, angleChange );
3173
3174 if( m_geometry_cache )
3175 m_geometry_cache->text_excluded_bbox_timestamp = 0;
3176
3177 if( m_courtyard_cache )
3178 {
3179 m_courtyard_cache->front.Rotate( angleChange, rotationCenter );
3180 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3181
3182 m_courtyard_cache->back.Rotate( angleChange, rotationCenter );
3183 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3184 }
3185
3186 if( m_geometry_cache )
3187 m_geometry_cache->hull.Rotate( angleChange, rotationCenter );
3188}
3189
3190
3191BOARD_ITEM* FOOTPRINT::Duplicate( bool addToParentGroup, BOARD_COMMIT* aCommit ) const
3192{
3193 FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate( addToParentGroup, aCommit ) );
3194
3195 dupe->RunOnChildren( [&]( BOARD_ITEM* child )
3196 {
3197 child->ResetUuidDirect();
3198 },
3200
3201 return dupe;
3202}
3203
3204
3205BOARD_ITEM* FOOTPRINT::DuplicateItem( bool addToParentGroup, BOARD_COMMIT* aCommit,
3206 const BOARD_ITEM* aItem, bool addToFootprint )
3207{
3208 BOARD_ITEM* new_item = nullptr;
3209
3210 switch( aItem->Type() )
3211 {
3212 case PCB_PAD_T:
3213 {
3214 PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
3215 new_pad->ResetUuidDirect();
3216
3217 if( addToFootprint )
3218 m_pads.push_back( new_pad );
3219
3220 new_item = new_pad;
3221 break;
3222 }
3223
3224 case PCB_ZONE_T:
3225 {
3226 ZONE* new_zone = new ZONE( *static_cast<const ZONE*>( aItem ) );
3227 new_zone->ResetUuidDirect();
3228
3229 if( addToFootprint )
3230 m_zones.push_back( new_zone );
3231
3232 new_item = new_zone;
3233 break;
3234 }
3235
3236 case PCB_POINT_T:
3237 {
3238 PCB_POINT* new_point = new PCB_POINT( *static_cast<const PCB_POINT*>( aItem ) );
3239 new_point->ResetUuidDirect();
3240
3241 if( addToFootprint )
3242 m_points.push_back( new_point );
3243
3244 new_item = new_point;
3245 break;
3246 }
3247
3248 case PCB_FIELD_T:
3249 case PCB_TEXT_T:
3250 {
3251 PCB_TEXT* new_text = new PCB_TEXT( *static_cast<const PCB_TEXT*>( aItem ) );
3252 new_text->ResetUuidDirect();
3253
3254 if( aItem->Type() == PCB_FIELD_T )
3255 {
3256 switch( static_cast<const PCB_FIELD*>( aItem )->GetId() )
3257 {
3258 case FIELD_T::REFERENCE: new_text->SetText( wxT( "${REFERENCE}" ) ); break;
3259 case FIELD_T::VALUE: new_text->SetText( wxT( "${VALUE}" ) ); break;
3260 case FIELD_T::DATASHEET: new_text->SetText( wxT( "${DATASHEET}" ) ); break;
3261 default: break;
3262 }
3263 }
3264
3265 if( addToFootprint )
3266 Add( new_text );
3267
3268 new_item = new_text;
3269 break;
3270 }
3271
3272 case PCB_SHAPE_T:
3273 {
3274 PCB_SHAPE* new_shape = new PCB_SHAPE( *static_cast<const PCB_SHAPE*>( aItem ) );
3275 new_shape->ResetUuidDirect();
3276
3277 if( addToFootprint )
3278 Add( new_shape );
3279
3280 new_item = new_shape;
3281 break;
3282 }
3283
3284 case PCB_BARCODE_T:
3285 {
3286 PCB_BARCODE* new_barcode = new PCB_BARCODE( *static_cast<const PCB_BARCODE*>( aItem ) );
3287 new_barcode->ResetUuidDirect();
3288
3289 if( addToFootprint )
3290 Add( new_barcode );
3291
3292 new_item = new_barcode;
3293 break;
3294 }
3295
3297 {
3298 PCB_REFERENCE_IMAGE* new_image = new PCB_REFERENCE_IMAGE( *static_cast<const PCB_REFERENCE_IMAGE*>( aItem ) );
3299 new_image->ResetUuidDirect();
3300
3301 if( addToFootprint )
3302 Add( new_image );
3303
3304 new_item = new_image;
3305 break;
3306 }
3307
3308 case PCB_TEXTBOX_T:
3309 {
3310 PCB_TEXTBOX* new_textbox = new PCB_TEXTBOX( *static_cast<const PCB_TEXTBOX*>( aItem ) );
3311 new_textbox->ResetUuidDirect();
3312
3313 if( addToFootprint )
3314 Add( new_textbox );
3315
3316 new_item = new_textbox;
3317 break;
3318 }
3319
3320 case PCB_DIM_ALIGNED_T:
3321 case PCB_DIM_LEADER_T:
3322 case PCB_DIM_CENTER_T:
3323 case PCB_DIM_RADIAL_T:
3325 {
3326 PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate( addToParentGroup,
3327 aCommit ) );
3328
3329 if( addToFootprint )
3330 Add( dimension );
3331
3332 new_item = dimension;
3333 break;
3334 }
3335
3336 case PCB_GROUP_T:
3337 {
3338 PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate( addToParentGroup, aCommit );
3339
3340 if( addToFootprint )
3341 {
3342 group->RunOnChildren(
3343 [&]( BOARD_ITEM* aCurrItem )
3344 {
3345 Add( aCurrItem );
3346 },
3348
3349 Add( group );
3350 }
3351
3352 new_item = group;
3353 break;
3354 }
3355
3356 case PCB_FOOTPRINT_T:
3357 // Ignore the footprint itself
3358 break;
3359
3360 default:
3361 // Un-handled item for duplication
3362 wxFAIL_MSG( wxT( "Duplication not supported for items of class " ) + aItem->GetClass() );
3363 break;
3364 }
3365
3366 return new_item;
3367}
3368
3369
3370wxString FOOTPRINT::GetNextPadNumber( const wxString& aLastPadNumber ) const
3371{
3372 std::set<wxString> usedNumbers;
3373
3374 // Create a set of used pad numbers
3375 for( PAD* pad : m_pads )
3376 usedNumbers.insert( pad->GetNumber() );
3377
3378 // Pad numbers aren't technically reference designators, but the formatting is close enough
3379 // for these to give us what we need.
3380 wxString prefix = UTIL::GetRefDesPrefix( aLastPadNumber );
3381 int num = GetTrailingInt( aLastPadNumber );
3382
3383 while( usedNumbers.count( wxString::Format( wxT( "%s%d" ), prefix, num ) ) )
3384 num++;
3385
3386 return wxString::Format( wxT( "%s%d" ), prefix, num );
3387}
3388
3389
3390std::optional<const std::set<wxString>> FOOTPRINT::GetJumperPadGroup( const wxString& aPadNumber ) const
3391{
3392 for( const std::set<wxString>& group : m_jumperPadGroups )
3393 {
3394 if( group.contains( aPadNumber ) )
3395 return group;
3396 }
3397
3398 return std::nullopt;
3399}
3400
3401
3403{
3404 // Auto-position reference and value
3405 BOX2I bbox = GetBoundingBox( false );
3406 bbox.Inflate( pcbIUScale.mmToIU( 0.2 ) ); // Gap between graphics and text
3407
3408 if( Reference().GetPosition() == VECTOR2I( 0, 0 ) )
3409 {
3413
3414 Reference().SetX( bbox.GetCenter().x );
3415 Reference().SetY( bbox.GetTop() - Reference().GetTextSize().y / 2 );
3416 }
3417
3418 if( Value().GetPosition() == VECTOR2I( 0, 0 ) )
3419 {
3423
3424 Value().SetX( bbox.GetCenter().x );
3425 Value().SetY( bbox.GetBottom() + Value().GetTextSize().y / 2 );
3426 }
3427}
3428
3429
3431{
3432 const wxString& refdes = GetReference();
3433
3434 SetReference( wxString::Format( wxT( "%s%i" ),
3435 UTIL::GetRefDesPrefix( refdes ),
3436 GetTrailingInt( refdes ) + aDelta ) );
3437}
3438
3439
3440// Calculate the area of a PolySet, polygons with hole are allowed.
3441static double polygonArea( SHAPE_POLY_SET& aPolySet )
3442{
3443 // Ensure all outlines are closed, before calculating the SHAPE_POLY_SET area
3444 for( int ii = 0; ii < aPolySet.OutlineCount(); ii++ )
3445 {
3446 SHAPE_LINE_CHAIN& outline = aPolySet.Outline( ii );
3447 outline.SetClosed( true );
3448
3449 for( int jj = 0; jj < aPolySet.HoleCount( ii ); jj++ )
3450 aPolySet.Hole( ii, jj ).SetClosed( true );
3451 }
3452
3453 return aPolySet.Area();
3454}
3455
3456
3457double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLECTOR& aCollector )
3458{
3459 int textMargin = aCollector.GetGuide()->Accuracy();
3460 SHAPE_POLY_SET poly;
3461
3462 if( aItem->Type() == PCB_MARKER_T )
3463 {
3464 const PCB_MARKER* marker = static_cast<const PCB_MARKER*>( aItem );
3465 SHAPE_LINE_CHAIN markerShape;
3466
3467 marker->ShapeToPolygon( markerShape );
3468 return markerShape.Area();
3469 }
3470 else if( aItem->Type() == PCB_GROUP_T || aItem->Type() == PCB_GENERATOR_T )
3471 {
3472 double combinedArea = 0.0;
3473
3474 for( BOARD_ITEM* member : static_cast<const PCB_GROUP*>( aItem )->GetBoardItems() )
3475 combinedArea += GetCoverageArea( member, aCollector );
3476
3477 return combinedArea;
3478 }
3479 if( aItem->Type() == PCB_FOOTPRINT_T )
3480 {
3481 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
3482
3483 poly = footprint->GetBoundingHull();
3484 }
3485 else if( aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T )
3486 {
3487 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aItem );
3488
3489 text->TransformTextToPolySet( poly, textMargin, ARC_LOW_DEF, ERROR_INSIDE );
3490 }
3491 else if( aItem->Type() == PCB_TEXTBOX_T )
3492 {
3493 const PCB_TEXTBOX* tb = static_cast<const PCB_TEXTBOX*>( aItem );
3494
3495 tb->TransformTextToPolySet( poly, textMargin, ARC_LOW_DEF, ERROR_INSIDE );
3496 }
3497 else if( aItem->Type() == PCB_SHAPE_T )
3498 {
3499 // Approximate "linear" shapes with just their width squared, as we don't want to consider
3500 // a linear shape as being much bigger than another for purposes of selection filtering
3501 // just because it happens to be really long.
3502
3503 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
3504
3505 switch( shape->GetShape() )
3506 {
3507 case SHAPE_T::SEGMENT:
3508 case SHAPE_T::ARC:
3509 case SHAPE_T::BEZIER:
3510 return shape->GetWidth() * shape->GetWidth();
3511
3512 case SHAPE_T::RECTANGLE:
3513 case SHAPE_T::CIRCLE:
3514 case SHAPE_T::POLY:
3515 {
3516 if( !shape->IsAnyFill() )
3517 return shape->GetWidth() * shape->GetWidth();
3518
3520 }
3521
3522 default:
3524 }
3525 }
3526 else if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
3527 {
3528 double width = static_cast<const PCB_TRACK*>( aItem )->GetWidth();
3529 return width * width;
3530 }
3531 else if( aItem->Type() == PCB_PAD_T )
3532 {
3533 static_cast<const PAD*>( aItem )->Padstack().ForEachUniqueLayer(
3534 [&]( PCB_LAYER_ID aLayer )
3535 {
3536 SHAPE_POLY_SET layerPoly;
3537 aItem->TransformShapeToPolygon( layerPoly, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
3538 poly.BooleanAdd( layerPoly );
3539 } );
3540 }
3541 else
3542 {
3544 }
3545
3546 return polygonArea( poly );
3547}
3548
3549
3550double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
3551{
3552 int textMargin = aCollector.GetGuide()->Accuracy();
3553
3554 SHAPE_POLY_SET footprintRegion( GetBoundingHull() );
3555 SHAPE_POLY_SET coveredRegion;
3556
3558
3559 TransformFPShapesToPolySet( coveredRegion, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF,
3561 true, /* include text */
3562 false, /* include shapes */
3563 false /* include private items */ );
3564
3565 for( int i = 0; i < aCollector.GetCount(); ++i )
3566 {
3567 const BOARD_ITEM* item = aCollector[i];
3568
3569 switch( item->Type() )
3570 {
3571 case PCB_FIELD_T:
3572 case PCB_TEXT_T:
3573 case PCB_TEXTBOX_T:
3574 case PCB_SHAPE_T:
3575 case PCB_BARCODE_T:
3576 case PCB_TRACE_T:
3577 case PCB_ARC_T:
3578 case PCB_VIA_T:
3579 if( item->GetParent() != this )
3580 {
3581 item->TransformShapeToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
3582 ERROR_OUTSIDE );
3583 }
3584 break;
3585
3586 case PCB_FOOTPRINT_T:
3587 if( item != this )
3588 {
3589 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( item );
3590 coveredRegion.AddOutline( footprint->GetBoundingHull().Outline( 0 ) );
3591 }
3592 break;
3593
3594 default:
3595 break;
3596 }
3597 }
3598
3599 coveredRegion.BooleanIntersection( footprintRegion );
3600
3601 double footprintRegionArea = polygonArea( footprintRegion );
3602 double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion );
3603 double coveredArea = footprintRegionArea - uncoveredRegionArea;
3604
3605 // Avoid div-by-zero (this will result in the disambiguate dialog)
3606 if( footprintRegionArea == 0 )
3607 return 1.0;
3608
3609 double ratio = coveredArea / footprintRegionArea;
3610
3611 // Test for negative ratio (should not occur).
3612 // better to be conservative (this will result in the disambiguate dialog)
3613 if( ratio < 0.0 )
3614 return 1.0;
3615
3616 return std::min( ratio, 1.0 );
3617}
3618
3619
3620std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
3621{
3622 std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
3623
3624 // There are several possible interpretations here:
3625 // 1) the bounding box (without or without invisible items)
3626 // 2) just the pads and "edges" (ie: non-text graphic items)
3627 // 3) the courtyard
3628
3629 // We'll go with (2) for now, unless the caller is clearly looking for (3)
3630
3631 if( aLayer == F_CrtYd || aLayer == B_CrtYd )
3632 {
3633 const SHAPE_POLY_SET& courtyard = GetCourtyard( aLayer );
3634
3635 if( courtyard.OutlineCount() == 0 ) // malformed/empty polygon
3636 return shape;
3637
3638 shape->AddShape( new SHAPE_SIMPLE( courtyard.COutline( 0 ) ) );
3639 }
3640 else
3641 {
3642 for( PAD* pad : Pads() )
3643 shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
3644
3645 for( BOARD_ITEM* item : GraphicalItems() )
3646 {
3647 if( item->Type() == PCB_SHAPE_T )
3648 shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
3649 else if( item->Type() == PCB_BARCODE_T )
3650 shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
3651 }
3652 }
3653
3654 return shape;
3655}
3656
3657
3659{
3660 std::lock_guard<std::mutex> lock( m_courtyard_cache_mutex );
3661
3663 || m_courtyard_cache->front_hash != m_courtyard_cache->front.GetHash()
3664 || m_courtyard_cache->back_hash != m_courtyard_cache->back.GetHash() )
3665 {
3666 const_cast<FOOTPRINT*>(this)->BuildCourtyardCaches();
3667 }
3668
3669 return GetCachedCourtyard( aLayer );
3670}
3671
3672
3674{
3675 if( !m_courtyard_cache )
3676 m_courtyard_cache = std::make_unique<FOOTPRINT_COURTYARD_CACHE_DATA>();
3677
3678 if( IsBackLayer( aLayer ) )
3679 return m_courtyard_cache->back;
3680 else
3681 return m_courtyard_cache->front;
3682}
3683
3684
3686{
3687 if( !m_courtyard_cache )
3688 m_courtyard_cache = std::make_unique<FOOTPRINT_COURTYARD_CACHE_DATA>();
3689
3690 m_courtyard_cache->front.RemoveAllContours();
3691 m_courtyard_cache->back.RemoveAllContours();
3693
3694 // Build the courtyard area from graphic items on the courtyard.
3695 // Only PCB_SHAPE_T have meaning, graphic texts are ignored.
3696 // Collect items:
3697 std::vector<PCB_SHAPE*> list_front;
3698 std::vector<PCB_SHAPE*> list_back;
3699 std::map<int, int> front_width_histogram;
3700 std::map<int, int> back_width_histogram;
3701
3702 for( BOARD_ITEM* item : GraphicalItems() )
3703 {
3704 if( item->GetLayer() == B_CrtYd && item->Type() == PCB_SHAPE_T )
3705 {
3706 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3707 list_back.push_back( shape );
3708 back_width_histogram[ shape->GetStroke().GetWidth() ]++;
3709 }
3710
3711 if( item->GetLayer() == F_CrtYd && item->Type() == PCB_SHAPE_T )
3712 {
3713 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3714 list_front.push_back( shape );
3715 front_width_histogram[ shape->GetStroke().GetWidth() ]++;
3716 }
3717 }
3718
3719 if( !list_front.size() && !list_back.size() )
3720 return;
3721
3722 int maxError = pcbIUScale.mmToIU( 0.005 ); // max error for polygonization
3723 int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
3724
3725 if( ConvertOutlineToPolygon( list_front, m_courtyard_cache->front, maxError, chainingEpsilon,
3726 true, aErrorHandler ) )
3727 {
3728 int width = 0;
3729
3730 // Touching courtyards, or courtyards -at- the clearance distance are legal.
3731 // Use maxError here because that is the allowed deviation when transforming arcs/circles to
3732 // polygons.
3733 m_courtyard_cache->front.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
3734
3735 m_courtyard_cache->front.CacheTriangulation();
3736 auto max = std::max_element( front_width_histogram.begin(), front_width_histogram.end(),
3737 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
3738 {
3739 return a.second < b.second;
3740 } );
3741
3742 if( max != front_width_histogram.end() )
3743 width = max->first;
3744
3745 if( width == 0 )
3746 width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH );
3747
3748 if( m_courtyard_cache->front.OutlineCount() > 0 )
3749 m_courtyard_cache->front.Outline( 0 ).SetWidth( width );
3750 }
3751 else
3752 {
3754 }
3755
3756 if( ConvertOutlineToPolygon( list_back, m_courtyard_cache->back, maxError, chainingEpsilon, true,
3757 aErrorHandler ) )
3758 {
3759 int width = 0;
3760
3761 // Touching courtyards, or courtyards -at- the clearance distance are legal.
3762 m_courtyard_cache->back.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
3763
3764 m_courtyard_cache->back.CacheTriangulation();
3765 auto max = std::max_element( back_width_histogram.begin(), back_width_histogram.end(),
3766 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
3767 {
3768 return a.second < b.second;
3769 } );
3770
3771 if( max != back_width_histogram.end() )
3772 width = max->first;
3773
3774 if( width == 0 )
3775 width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH );
3776
3777 if( m_courtyard_cache->back.OutlineCount() > 0 )
3778 m_courtyard_cache->back.Outline( 0 ).SetWidth( width );
3779 }
3780 else
3781 {
3783 }
3784
3785 m_courtyard_cache->front_hash = m_courtyard_cache->front.GetHash();
3786 m_courtyard_cache->back_hash = m_courtyard_cache->back.GetHash();
3787}
3788
3789
3791{
3792 m_netTieCache.clear();
3793 std::map<wxString, int> map = MapPadNumbersToNetTieGroups();
3794 std::map<PCB_LAYER_ID, std::vector<PCB_SHAPE*>> layer_shapes;
3795 BOARD* board = GetBoard();
3796
3797 std::for_each( m_drawings.begin(), m_drawings.end(),
3798 [&]( BOARD_ITEM* item )
3799 {
3800 if( item->Type() != PCB_SHAPE_T )
3801 return;
3802
3803 for( PCB_LAYER_ID layer : item->GetLayerSet() )
3804 {
3805 if( !IsCopperLayer( layer ) )
3806 continue;
3807
3808 if( board && !board->GetEnabledLayers().Contains( layer ) )
3809 continue;
3810
3811 layer_shapes[layer].push_back( static_cast<PCB_SHAPE*>( item ) );
3812 }
3813 } );
3814
3815 for( size_t ii = 0; ii < m_pads.size(); ++ii )
3816 {
3817 PAD* pad = m_pads[ ii ];
3818 bool has_nettie = false;
3819
3820 auto it = map.find( pad->GetNumber() );
3821
3822 if( it == map.end() || it->second < 0 )
3823 continue;
3824
3825 for( size_t jj = ii + 1; jj < m_pads.size(); ++jj )
3826 {
3827 PAD* other = m_pads[ jj ];
3828
3829 auto it2 = map.find( other->GetNumber() );
3830
3831 if( it2 == map.end() || it2->second < 0 )
3832 continue;
3833
3834 if( it2->second == it->second )
3835 {
3836 m_netTieCache[pad].insert( pad->GetNetCode() );
3837 m_netTieCache[pad].insert( other->GetNetCode() );
3838 m_netTieCache[other].insert( other->GetNetCode() );
3839 m_netTieCache[other].insert( pad->GetNetCode() );
3840 has_nettie = true;
3841 }
3842 }
3843
3844 if( !has_nettie )
3845 continue;
3846
3847 for( auto& [ layer, shapes ] : layer_shapes )
3848 {
3849 auto pad_shape = pad->GetEffectiveShape( layer );
3850
3851 for( auto other_shape : shapes )
3852 {
3853 auto shape = other_shape->GetEffectiveShape( layer );
3854
3855 if( pad_shape->Collide( shape.get() ) )
3856 {
3857 std::set<int>& nettie = m_netTieCache[pad];
3858 m_netTieCache[other_shape].insert( nettie.begin(), nettie.end() );
3859 }
3860 }
3861 }
3862 }
3863}
3864
3865
3866std::map<wxString, int> FOOTPRINT::MapPadNumbersToNetTieGroups() const
3867{
3868 std::map<wxString, int> padNumberToGroupIdxMap;
3869
3870 for( const PAD* pad : m_pads )
3871 padNumberToGroupIdxMap[ pad->GetNumber() ] = -1;
3872
3873 auto processPad =
3874 [&]( wxString aPad, int aGroup )
3875 {
3876 aPad.Trim( true ).Trim( false );
3877
3878 if( !aPad.IsEmpty() )
3879 padNumberToGroupIdxMap[ aPad ] = aGroup;
3880 };
3881
3882 for( int ii = 0; ii < (int) m_netTiePadGroups.size(); ++ii )
3883 {
3884 wxString group( m_netTiePadGroups[ ii ] );
3885 bool esc = false;
3886 wxString pad;
3887
3888 for( wxUniCharRef ch : group )
3889 {
3890 if( esc )
3891 {
3892 esc = false;
3893 pad.Append( ch );
3894 continue;
3895 }
3896
3897 switch( static_cast<unsigned char>( ch ) )
3898 {
3899 case '\\':
3900 esc = true;
3901 break;
3902
3903 case ',':
3904 processPad( pad, ii );
3905 pad.Clear();
3906 break;
3907
3908 default:
3909 pad.Append( ch );
3910 break;
3911 }
3912 }
3913
3914 processPad( pad, ii );
3915 }
3916
3917 return padNumberToGroupIdxMap;
3918}
3919
3920
3921std::vector<PAD*> FOOTPRINT::GetNetTiePads( PAD* aPad ) const
3922{
3923 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
3924 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
3925
3926 std::map<wxString, int> padToNetTieGroupMap = MapPadNumbersToNetTieGroups();
3927 int groupIdx = padToNetTieGroupMap[ aPad->GetNumber() ];
3928 std::vector<PAD*> otherPads;
3929
3930 if( groupIdx >= 0 )
3931 {
3932 for( PAD* pad : m_pads )
3933 {
3934 if( padToNetTieGroupMap[ pad->GetNumber() ] == groupIdx )
3935 otherPads.push_back( pad );
3936 }
3937 }
3938
3939 return otherPads;
3940}
3941
3942
3943void FOOTPRINT::CheckFootprintAttributes( const std::function<void( const wxString& )>& aErrorHandler )
3944{
3945 int likelyAttr = ( GetLikelyAttribute() & ( FP_SMD | FP_THROUGH_HOLE ) );
3946 int setAttr = ( GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
3947
3948 if( setAttr && likelyAttr && setAttr != likelyAttr )
3949 {
3950 wxString msg;
3951
3952 switch( likelyAttr )
3953 {
3954 case FP_THROUGH_HOLE:
3955 msg.Printf( _( "(expected 'Through hole'; actual '%s')" ), GetTypeName() );
3956 break;
3957 case FP_SMD:
3958 msg.Printf( _( "(expected 'SMD'; actual '%s')" ), GetTypeName() );
3959 break;
3960 }
3961
3962 if( aErrorHandler )
3963 (aErrorHandler)( msg );
3964 }
3965}
3966
3967
3969 const std::function<void( const PAD*, int,
3970 const wxString& )>& aErrorHandler )
3971{
3972 if( aErrorHandler == nullptr )
3973 return;
3974
3975 for( PAD* pad: Pads() )
3976 {
3977 pad->CheckPad( aUnitsProvider, false,
3978 [&]( int errorCode, const wxString& msg )
3979 {
3980 aErrorHandler( pad, errorCode, msg );
3981 } );
3982 }
3983}
3984
3985
3986void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const PAD*,
3987 int aErrorCode,
3988 const VECTOR2I& )>& aErrorHandler )
3989{
3990 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
3991
3992 for( PAD* pad : Pads() )
3993 {
3994 std::vector<PAD*> netTiePads = GetNetTiePads( pad );
3995
3996 for( PAD* other : Pads() )
3997 {
3998 if( other == pad )
3999 continue;
4000
4001 // store canonical order so we don't collide in both directions (a:b and b:a)
4002 PAD* a = pad;
4003 PAD* b = other;
4004
4005 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
4006 std::swap( a, b );
4007
4008 if( checkedPairs.find( { a, b } ) == checkedPairs.end() )
4009 {
4010 checkedPairs[ { a, b } ] = 1;
4011
4012 if( pad->HasDrilledHole() && other->HasDrilledHole() )
4013 {
4014 VECTOR2I pos = pad->GetPosition();
4015
4016 if( pad->GetPosition() == other->GetPosition() )
4017 {
4018 aErrorHandler( pad, other, DRCE_DRILLED_HOLES_COLOCATED, pos );
4019 }
4020 else
4021 {
4022 std::shared_ptr<SHAPE_SEGMENT> holeA = pad->GetEffectiveHoleShape();
4023 std::shared_ptr<SHAPE_SEGMENT> holeB = other->GetEffectiveHoleShape();
4024
4025 if( holeA->Collide( holeB->GetSeg(), 0 ) )
4026 aErrorHandler( pad, other, DRCE_DRILLED_HOLES_TOO_CLOSE, pos );
4027 }
4028 }
4029
4030 if( pad->SameLogicalPadAs( other ) || alg::contains( netTiePads, other ) )
4031 continue;
4032
4033 if( !( ( pad->GetLayerSet() & other->GetLayerSet() ) & LSET::AllCuMask() ).any() )
4034 continue;
4035
4036 if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
4037 {
4038 VECTOR2I pos;
4039
4040 for( PCB_LAYER_ID l : pad->Padstack().RelevantShapeLayers( other->Padstack() ) )
4041 {
4042 SHAPE* padShape = pad->GetEffectiveShape( l ).get();
4043 SHAPE* otherShape = other->GetEffectiveShape( l ).get();
4044
4045 if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
4046 aErrorHandler( pad, other, DRCE_SHORTING_ITEMS, pos );
4047 }
4048 }
4049 }
4050 }
4051 }
4052}
4053
4054
4055void FOOTPRINT::CheckNetTies( const std::function<void( const BOARD_ITEM* aItem,
4056 const BOARD_ITEM* bItem,
4057 const BOARD_ITEM* cItem,
4058 const VECTOR2I& )>& aErrorHandler )
4059{
4060 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
4061 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
4062
4063 std::map<wxString, int> padNumberToGroupIdxMap = MapPadNumbersToNetTieGroups();
4064
4065 // Now collect all the footprint items which are on copper layers
4066
4067 std::vector<BOARD_ITEM*> copperItems;
4068
4069 for( BOARD_ITEM* item : m_drawings )
4070 {
4071 if( item->IsOnCopperLayer() )
4072 copperItems.push_back( item );
4073
4074 item->RunOnChildren(
4075 [&]( BOARD_ITEM* descendent )
4076 {
4077 if( descendent->IsOnCopperLayer() )
4078 copperItems.push_back( descendent );
4079 },
4081 }
4082
4083 for( ZONE* zone : m_zones )
4084 {
4085 if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
4086 copperItems.push_back( zone );
4087 }
4088
4089 for( PCB_FIELD* field : m_fields )
4090 {
4091 if( field->IsOnCopperLayer() )
4092 copperItems.push_back( field );
4093 }
4094
4095 for( PCB_LAYER_ID layer : { F_Cu, In1_Cu, B_Cu } )
4096 {
4097 // Next, build a polygon-set for the copper on this layer. We don't really care about
4098 // nets here, we just want to end up with a set of outlines describing the distinct
4099 // copper polygons of the footprint.
4100
4101 SHAPE_POLY_SET copperOutlines;
4102 std::map<int, std::vector<const PAD*>> outlineIdxToPadsMap;
4103
4104 for( BOARD_ITEM* item : copperItems )
4105 {
4106 if( item->IsOnLayer( layer ) )
4107 item->TransformShapeToPolygon( copperOutlines, layer, 0, GetMaxError(), ERROR_OUTSIDE );
4108 }
4109
4110 copperOutlines.Simplify();
4111
4112 // Index each pad to the outline in the set that it is part of.
4113
4114 for( const PAD* pad : m_pads )
4115 {
4116 for( int ii = 0; ii < copperOutlines.OutlineCount(); ++ii )
4117 {
4118 if( pad->GetEffectiveShape( layer )->Collide( &copperOutlines.Outline( ii ), 0 ) )
4119 outlineIdxToPadsMap[ ii ].emplace_back( pad );
4120 }
4121 }
4122
4123 // Finally, ensure that each outline which contains multiple pads has all its pads
4124 // listed in an allowed-shorting group.
4125
4126 for( const auto& [ outlineIdx, pads ] : outlineIdxToPadsMap )
4127 {
4128 if( pads.size() > 1 )
4129 {
4130 const PAD* firstPad = pads[0];
4131 int firstGroupIdx = padNumberToGroupIdxMap[ firstPad->GetNumber() ];
4132
4133 for( size_t ii = 1; ii < pads.size(); ++ii )
4134 {
4135 const PAD* thisPad = pads[ii];
4136 int thisGroupIdx = padNumberToGroupIdxMap[ thisPad->GetNumber() ];
4137
4138 if( thisGroupIdx < 0 || thisGroupIdx != firstGroupIdx )
4139 {
4140 BOARD_ITEM* shortingItem = nullptr;
4141 VECTOR2I pos = ( firstPad->GetPosition() + thisPad->GetPosition() ) / 2;
4142
4143 pos = copperOutlines.Outline( outlineIdx ).NearestPoint( pos );
4144
4145 for( BOARD_ITEM* item : copperItems )
4146 {
4147 if( item->HitTest( pos, 1 ) )
4148 {
4149 shortingItem = item;
4150 break;
4151 }
4152 }
4153
4154 if( shortingItem )
4155 aErrorHandler( shortingItem, firstPad, thisPad, pos );
4156 else
4157 aErrorHandler( firstPad, thisPad, nullptr, pos );
4158 }
4159 }
4160 }
4161 }
4162 }
4163}
4164
4165
4166void FOOTPRINT::CheckNetTiePadGroups( const std::function<void( const wxString& )>& aErrorHandler )
4167{
4168 std::set<wxString> padNumbers;
4169 wxString msg;
4170
4171 for( const auto& [ padNumber, _ ] : MapPadNumbersToNetTieGroups() )
4172 {
4173 const PAD* pad = FindPadByNumber( padNumber );
4174
4175 if( !pad )
4176 {
4177 msg.Printf( _( "(net-tie pad group contains unknown pad number %s)" ), padNumber );
4178 aErrorHandler( msg );
4179 }
4180 else if( !padNumbers.insert( pad->GetNumber() ).second )
4181 {
4182 msg.Printf( _( "(pad %s appears in more than one net-tie pad group)" ), padNumber );
4183 aErrorHandler( msg );
4184 }
4185 }
4186}
4187
4188
4189void FOOTPRINT::CheckClippedSilk( const std::function<void( BOARD_ITEM* aItemA,
4190 BOARD_ITEM* aItemB,
4191 const VECTOR2I& aPt )>& aErrorHandler )
4192{
4193 auto checkColliding =
4194 [&]( BOARD_ITEM* item, BOARD_ITEM* other )
4195 {
4196 for( PCB_LAYER_ID silk : { F_SilkS, B_SilkS } )
4197 {
4198 PCB_LAYER_ID mask = silk == F_SilkS ? F_Mask : B_Mask;
4199
4200 if( !item->IsOnLayer( silk ) || !other->IsOnLayer( mask ) )
4201 continue;
4202
4203 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( silk );
4204 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( mask );
4205 int actual;
4206 VECTOR2I pos;
4207
4208 if( itemShape->Collide( otherShape.get(), 0, &actual, &pos ) )
4209 aErrorHandler( item, other, pos );
4210 }
4211 };
4212
4213 for( BOARD_ITEM* item : m_drawings )
4214 {
4215 for( BOARD_ITEM* other : m_drawings )
4216 {
4217 if( other != item )
4218 checkColliding( item, other );
4219 }
4220
4221 for( PAD* pad : m_pads )
4222 checkColliding( item, pad );
4223 }
4224}
4225
4226
4228{
4229 wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );
4230
4231 FOOTPRINT* image = static_cast<FOOTPRINT*>( aImage );
4232
4233 std::swap( *this, *image );
4234
4236 [&]( BOARD_ITEM* child )
4237 {
4238 child->SetParent( this );
4239 },
4241
4242 image->RunOnChildren(
4243 [&]( BOARD_ITEM* child )
4244 {
4245 child->SetParent( image );
4246 },
4248}
4249
4250
4252{
4253 for( PAD* pad : Pads() )
4254 {
4255 if( pad->GetAttribute() != PAD_ATTRIB::SMD )
4256 return true;
4257 }
4258
4259 return false;
4260}
4261
4262
4263bool FOOTPRINT::operator==( const BOARD_ITEM& aOther ) const
4264{
4265 if( aOther.Type() != PCB_FOOTPRINT_T )
4266 return false;
4267
4268 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
4269
4270 return *this == other;
4271}
4272
4273
4274bool FOOTPRINT::operator==( const FOOTPRINT& aOther ) const
4275{
4276 if( m_pads.size() != aOther.m_pads.size() )
4277 return false;
4278
4279 for( size_t ii = 0; ii < m_pads.size(); ++ii )
4280 {
4281 if( !( *m_pads[ii] == *aOther.m_pads[ii] ) )
4282 return false;
4283 }
4284
4285 if( m_drawings.size() != aOther.m_drawings.size() )
4286 return false;
4287
4288 for( size_t ii = 0; ii < m_drawings.size(); ++ii )
4289 {
4290 if( !( *m_drawings[ii] == *aOther.m_drawings[ii] ) )
4291 return false;
4292 }
4293
4294 if( m_zones.size() != aOther.m_zones.size() )
4295 return false;
4296
4297 for( size_t ii = 0; ii < m_zones.size(); ++ii )
4298 {
4299 if( !( *m_zones[ii] == *aOther.m_zones[ii] ) )
4300 return false;
4301 }
4302
4303 if( m_points.size() != aOther.m_points.size() )
4304 return false;
4305
4306 // Compare fields in ordinally-sorted order
4307 std::vector<PCB_FIELD*> fields, otherFields;
4308
4309 GetFields( fields, false );
4310 aOther.GetFields( otherFields, false );
4311
4312 if( fields.size() != otherFields.size() )
4313 return false;
4314
4315 for( size_t ii = 0; ii < fields.size(); ++ii )
4316 {
4317 if( fields[ii] )
4318 {
4319 if( !( *fields[ii] == *otherFields[ii] ) )
4320 return false;
4321 }
4322 }
4323
4324 return true;
4325}
4326
4327
4328double FOOTPRINT::Similarity( const BOARD_ITEM& aOther ) const
4329{
4330 if( aOther.Type() != PCB_FOOTPRINT_T )
4331 return 0.0;
4332
4333 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
4334
4335 double similarity = 1.0;
4336
4337 for( const PAD* pad : m_pads)
4338 {
4339 const PAD* otherPad = other.FindPadByNumber( pad->GetNumber() );
4340
4341 if( !otherPad )
4342 continue;
4343
4344 similarity *= pad->Similarity( *otherPad );
4345 }
4346
4347 return similarity;
4348}
4349
4350
4354static constexpr std::optional<bool> cmp_points_opt( const VECTOR2I& aPtA, const VECTOR2I& aPtB )
4355{
4356 if( aPtA.x != aPtB.x )
4357 return aPtA.x < aPtB.x;
4358
4359 if( aPtA.y != aPtB.y )
4360 return aPtA.y < aPtB.y;
4361
4362 return std::nullopt;
4363}
4364
4365
4366bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* itemA, const BOARD_ITEM* itemB ) const
4367{
4368 if( itemA->Type() != itemB->Type() )
4369 return itemA->Type() < itemB->Type();
4370
4371 if( itemA->GetLayer() != itemB->GetLayer() )
4372 return itemA->GetLayer() < itemB->GetLayer();
4373
4374 switch( itemA->Type() )
4375 {
4376 case PCB_SHAPE_T:
4377 {
4378 const PCB_SHAPE* dwgA = static_cast<const PCB_SHAPE*>( itemA );
4379 const PCB_SHAPE* dwgB = static_cast<const PCB_SHAPE*>( itemB );
4380
4381 if( dwgA->GetShape() != dwgB->GetShape() )
4382 return dwgA->GetShape() < dwgB->GetShape();
4383
4384 // GetStart() and GetEnd() have no meaning with polygons.
4385 // We cannot use them for sorting polygons
4386 if( dwgA->GetShape() != SHAPE_T::POLY )
4387 {
4388 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetStart(), dwgB->GetStart() ) )
4389 return *cmp;
4390
4391 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetEnd(), dwgB->GetEnd() ) )
4392 return *cmp;
4393 }
4394
4395 if( dwgA->GetShape() == SHAPE_T::ARC )
4396 {
4397 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetCenter(), dwgB->GetCenter() ) )
4398 return *cmp;
4399 }
4400 else if( dwgA->GetShape() == SHAPE_T::BEZIER )
4401 {
4402 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetBezierC1(), dwgB->GetBezierC1() ) )
4403 return *cmp;
4404
4405 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetBezierC2(), dwgB->GetBezierC2() ) )
4406 return *cmp;
4407 }
4408 else if( dwgA->GetShape() == SHAPE_T::POLY )
4409 {
4410 if( dwgA->GetPolyShape().TotalVertices() != dwgB->GetPolyShape().TotalVertices() )
4411 return dwgA->GetPolyShape().TotalVertices() < dwgB->GetPolyShape().TotalVertices();
4412
4413 for( int ii = 0; ii < dwgA->GetPolyShape().TotalVertices(); ++ii )
4414 {
4415 if( std::optional<bool> cmp =
4416 cmp_points_opt( dwgA->GetPolyShape().CVertex( ii ), dwgB->GetPolyShape().CVertex( ii ) ) )
4417 {
4418 return *cmp;
4419 }
4420 }
4421 }
4422 else if( dwgA->GetShape() == SHAPE_T::ELLIPSE || dwgA->GetShape() == SHAPE_T::ELLIPSE_ARC )
4423 {
4424 if( std::optional<bool> cmp = cmp_points_opt( dwgA->GetEllipseCenter(), dwgB->GetEllipseCenter() ) )
4425 return *cmp;
4426
4427 if( dwgA->GetEllipseMajorRadius() != dwgB->GetEllipseMajorRadius() )
4428 return dwgA->GetEllipseMajorRadius() < dwgB->GetEllipseMajorRadius();
4429
4430 if( dwgA->GetEllipseMinorRadius() != dwgB->GetEllipseMinorRadius() )
4431 return dwgA->GetEllipseMinorRadius() < dwgB->GetEllipseMinorRadius();
4432
4435
4436 if( dwgA->GetShape() == SHAPE_T::ELLIPSE_ARC )
4437 {
4442
4444 return dwgA->GetEllipseEndAngle().AsTenthsOfADegree()
4446 }
4447 }
4448
4449 if( dwgA->GetWidth() != dwgB->GetWidth() )
4450 return dwgA->GetWidth() < dwgB->GetWidth();
4451
4452 break;
4453 }
4454 case PCB_TEXT_T:
4455 {
4456 const PCB_TEXT& textA = static_cast<const PCB_TEXT&>( *itemA );
4457 const PCB_TEXT& textB = static_cast<const PCB_TEXT&>( *itemB );
4458
4459 if( std::optional<bool> cmp = cmp_points_opt( textA.GetPosition(), textB.GetPosition() ) )
4460 return *cmp;
4461
4462 if( textA.GetTextAngle() != textB.GetTextAngle() )
4463 return textA.GetTextAngle() < textB.GetTextAngle();
4464
4465 if( std::optional<bool> cmp = cmp_points_opt( textA.GetTextSize(), textB.GetTextSize() ) )
4466 return *cmp;
4467
4468 if( textA.GetTextThickness() != textB.GetTextThickness() )
4469 return textA.GetTextThickness() < textB.GetTextThickness();
4470
4471 if( textA.IsBold() != textB.IsBold() )
4472 return textA.IsBold() < textB.IsBold();
4473
4474 if( textA.IsItalic() != textB.IsItalic() )
4475 return textA.IsItalic() < textB.IsItalic();
4476
4477 if( textA.IsMirrored() != textB.IsMirrored() )
4478 return textA.IsMirrored() < textB.IsMirrored();
4479
4480 if( textA.GetLineSpacing() != textB.GetLineSpacing() )
4481 return textA.GetLineSpacing() < textB.GetLineSpacing();
4482
4483 if( textA.GetText() != textB.GetText() )
4484 return textA.GetText().Cmp( textB.GetText() ) < 0;
4485
4486 break;
4487 }
4488 default:
4489 {
4490 // These items don't have their own specific sorting criteria.
4491 break;
4492 }
4493 }
4494
4495 if( itemA->m_Uuid != itemB->m_Uuid )
4496 return itemA->m_Uuid < itemB->m_Uuid;
4497
4498 return itemA < itemB;
4499}
4500
4501
4502bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) const
4503{
4504 if( aFirst->GetNumber() != aSecond->GetNumber() )
4505 return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0;
4506
4507 if( std::optional<bool> cmp = cmp_points_opt( aFirst->GetFPRelativePosition(), aSecond->GetFPRelativePosition() ) )
4508 return *cmp;
4509
4510 std::optional<bool> padCopperMatches;
4511
4512 // Pick the "most complex" padstack to iterate
4513 const PAD* checkPad = aFirst;
4514
4515 if( aSecond->Padstack().Mode() == PADSTACK::MODE::CUSTOM
4516 || ( aSecond->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK &&
4517 aFirst->Padstack().Mode() == PADSTACK::MODE::NORMAL ) )
4518 {
4519 checkPad = aSecond;
4520 }
4521
4522 checkPad->Padstack().ForEachUniqueLayer(
4523 [&]( PCB_LAYER_ID aLayer )
4524 {
4525 if( aFirst->GetSize( aLayer ).x != aSecond->GetSize( aLayer ).x )
4526 padCopperMatches = aFirst->GetSize( aLayer ).x < aSecond->GetSize( aLayer ).x;
4527 else if( aFirst->GetSize( aLayer ).y != aSecond->GetSize( aLayer ).y )
4528 padCopperMatches = aFirst->GetSize( aLayer ).y < aSecond->GetSize( aLayer ).y;
4529 else if( aFirst->GetShape( aLayer ) != aSecond->GetShape( aLayer ) )
4530 padCopperMatches = aFirst->GetShape( aLayer ) < aSecond->GetShape( aLayer );
4531 } );
4532
4533 if( padCopperMatches.has_value() )
4534 return *padCopperMatches;
4535
4536 if( aFirst->GetLayerSet() != aSecond->GetLayerSet() )
4537 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
4538
4539 if( aFirst->m_Uuid != aSecond->m_Uuid )
4540 return aFirst->m_Uuid < aSecond->m_Uuid;
4541
4542 return aFirst < aSecond;
4543}
4544
4545
4546#if 0
4547bool FOOTPRINT::cmp_padstack::operator()( const PAD* aFirst, const PAD* aSecond ) const
4548{
4549 if( aFirst->GetSize().x != aSecond->GetSize().x )
4550 return aFirst->GetSize().x < aSecond->GetSize().x;
4551 if( aFirst->GetSize().y != aSecond->GetSize().y )
4552 return aFirst->GetSize().y < aSecond->GetSize().y;
4553
4554 if( aFirst->GetShape() != aSecond->GetShape() )
4555 return aFirst->GetShape() < aSecond->GetShape();
4556
4557 if( aFirst->GetLayerSet() != aSecond->GetLayerSet() )
4558 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
4559
4560 if( aFirst->GetDrillSizeX() != aSecond->GetDrillSizeX() )
4561 return aFirst->GetDrillSizeX() < aSecond->GetDrillSizeX();
4562
4563 if( aFirst->GetDrillSizeY() != aSecond->GetDrillSizeY() )
4564 return aFirst->GetDrillSizeY() < aSecond->GetDrillSizeY();
4565
4566 if( aFirst->GetDrillShape() != aSecond->GetDrillShape() )
4567 return aFirst->GetDrillShape() < aSecond->GetDrillShape();
4568
4569 if( aFirst->GetAttribute() != aSecond->GetAttribute() )
4570 return aFirst->GetAttribute() < aSecond->GetAttribute();
4571
4572 if( aFirst->GetOrientation() != aSecond->GetOrientation() )
4573 return aFirst->GetOrientation() < aSecond->GetOrientation();
4574
4575 if( aFirst->GetSolderMaskExpansion() != aSecond->GetSolderMaskExpansion() )
4576 return aFirst->GetSolderMaskExpansion() < aSecond->GetSolderMaskExpansion();
4577
4578 if( aFirst->GetSolderPasteMargin() != aSecond->GetSolderPasteMargin() )
4579 return aFirst->GetSolderPasteMargin() < aSecond->GetSolderPasteMargin();
4580
4581 if( aFirst->GetLocalSolderMaskMargin() != aSecond->GetLocalSolderMaskMargin() )
4582 return aFirst->GetLocalSolderMaskMargin() < aSecond->GetLocalSolderMaskMargin();
4583
4584 const std::shared_ptr<SHAPE_POLY_SET>& firstShape = aFirst->GetEffectivePolygon( ERROR_INSIDE );
4585 const std::shared_ptr<SHAPE_POLY_SET>& secondShape = aSecond->GetEffectivePolygon( ERROR_INSIDE );
4586
4587 if( firstShape->VertexCount() != secondShape->VertexCount() )
4588 return firstShape->VertexCount() < secondShape->VertexCount();
4589
4590 for( int ii = 0; ii < firstShape->VertexCount(); ++ii )
4591 {
4592 if( std::optional<bool> cmp = cmp_points_opt( firstShape->CVertex( ii ), secondShape->CVertex( ii ) ) )
4593 {
4594 return *cmp;
4595 }
4596 }
4597
4598 return false;
4599}
4600#endif
4601
4602
4603bool FOOTPRINT::cmp_zones::operator()( const ZONE* aFirst, const ZONE* aSecond ) const
4604{
4605 if( aFirst->GetAssignedPriority() != aSecond->GetAssignedPriority() )
4606 return aFirst->GetAssignedPriority() < aSecond->GetAssignedPriority();
4607
4608 if( aFirst->GetLayerSet() != aSecond->GetLayerSet() )
4609 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
4610
4611 if( aFirst->Outline()->TotalVertices() != aSecond->Outline()->TotalVertices() )
4612 return aFirst->Outline()->TotalVertices() < aSecond->Outline()->TotalVertices();
4613
4614 for( int ii = 0; ii < aFirst->Outline()->TotalVertices(); ++ii )
4615 {
4616 if( std::optional<bool> cmp =
4617 cmp_points_opt( aFirst->Outline()->CVertex( ii ), aSecond->Outline()->CVertex( ii ) ) )
4618 {
4619 return *cmp;
4620 }
4621 }
4622
4623 if( aFirst->m_Uuid != aSecond->m_Uuid )
4624 return aFirst->m_Uuid < aSecond->m_Uuid;
4625
4626 return aFirst < aSecond;
4627}
4628
4629
4631 int aMaxError, ERROR_LOC aErrorLoc ) const
4632{
4633 auto processPad =
4634 [&]( const PAD* pad, PCB_LAYER_ID padLayer )
4635 {
4636 VECTOR2I clearance( aClearance, aClearance );
4637
4638 switch( aLayer )
4639 {
4640 case F_Mask:
4641 case B_Mask:
4642 clearance.x += pad->GetSolderMaskExpansion( padLayer );
4643 clearance.y += pad->GetSolderMaskExpansion( padLayer );
4644 break;
4645
4646 case F_Paste:
4647 case B_Paste:
4648 clearance += pad->GetSolderPasteMargin( padLayer );
4649 break;
4650
4651 default:
4652 break;
4653 }
4654
4655 // Our standard TransformShapeToPolygon() routines can't handle differing x:y clearance
4656 // values (which get generated when a relative paste margin is used with an oblong pad).
4657 // So we apply this huge hack and fake a larger pad to run the transform on.
4658 // Of course being a hack it falls down when dealing with custom shape pads (where the
4659 // size is only the size of the anchor), so for those we punt and just use clearance.x.
4660
4661 if( ( clearance.x < 0 || clearance.x != clearance.y )
4662 && pad->GetShape( padLayer ) != PAD_SHAPE::CUSTOM )
4663 {
4664 VECTOR2I dummySize = pad->GetSize( padLayer ) + clearance + clearance;
4665
4666 if( dummySize.x <= 0 || dummySize.y <= 0 )
4667 return;
4668
4669 PAD dummy( *pad );
4670 dummy.SetSize( padLayer, dummySize );
4671 dummy.TransformShapeToPolygon( aBuffer, padLayer, 0, aMaxError, aErrorLoc );
4672 }
4673 else
4674 {
4675 pad->TransformShapeToPolygon( aBuffer, padLayer, clearance.x, aMaxError, aErrorLoc );
4676 }
4677 };
4678
4679 for( const PAD* pad : m_pads )
4680 {
4681 if( !pad->FlashLayer( aLayer ) )
4682 continue;
4683
4684 if( aLayer == UNDEFINED_LAYER )
4685 {
4686 pad->Padstack().ForEachUniqueLayer(
4687 [&]( PCB_LAYER_ID l )
4688 {
4689 processPad( pad, l );
4690 } );
4691 }
4692 else
4693 {
4694 processPad( pad, aLayer );
4695 }
4696 }
4697}
4698
4699
4701 int aError, ERROR_LOC aErrorLoc, bool aIncludeText,
4702 bool aIncludeShapes, bool aIncludePrivateItems ) const
4703{
4704 for( BOARD_ITEM* item : GraphicalItems() )
4705 {
4706 if( GetPrivateLayers().test( item->GetLayer() ) && !aIncludePrivateItems )
4707 continue;
4708
4709 if( item->Type() == PCB_TEXT_T && aIncludeText )
4710 {
4711 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
4712
4713 if( aLayer == UNDEFINED_LAYER || text->GetLayer() == aLayer )
4714 text->TransformTextToPolySet( aBuffer, aClearance, aError, aErrorLoc );
4715 }
4716
4717 if( item->Type() == PCB_TEXTBOX_T && aIncludeText )
4718 {
4719 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
4720
4721 if( aLayer == UNDEFINED_LAYER || textbox->GetLayer() == aLayer )
4722 {
4723 // border
4724 if( textbox->IsBorderEnabled() )
4725 textbox->PCB_SHAPE::TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
4726
4727 // text
4728 textbox->TransformTextToPolySet( aBuffer, 0, aError, aErrorLoc );
4729 }
4730 }
4731
4732 if( item->Type() == PCB_SHAPE_T && aIncludeShapes )
4733 {
4734 const PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
4735
4736 if( aLayer == UNDEFINED_LAYER || shape->GetLayer() == aLayer )
4737 shape->TransformShapeToPolySet( aBuffer, aLayer, 0, aError, aErrorLoc );
4738 }
4739
4740 if( item->Type() == PCB_BARCODE_T && aIncludeShapes )
4741 {
4742 const PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( item );
4743
4744 if( aLayer == UNDEFINED_LAYER || barcode->GetLayer() == aLayer )
4745 barcode->TransformShapeToPolySet( aBuffer, aLayer, 0, aError, aErrorLoc );
4746 }
4747 }
4748
4749 if( aIncludeText )
4750 {
4751 for( const PCB_FIELD* field : m_fields )
4752 {
4753 if( ( aLayer == UNDEFINED_LAYER || field->GetLayer() == aLayer ) && field->IsVisible() )
4754 field->TransformTextToPolySet( aBuffer, aClearance, aError, aErrorLoc );
4755 }
4756 }
4757}
4758
4759
4760std::set<KIFONT::OUTLINE_FONT*> FOOTPRINT::GetFonts() const
4761{
4763
4764 std::set<KIFONT::OUTLINE_FONT*> fonts;
4765
4766 auto processItem =
4767 [&]( BOARD_ITEM* item )
4768 {
4769 if( EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( item ) )
4770 {
4771 KIFONT::FONT* font = text->GetFont();
4772
4773 if( font && font->IsOutline() )
4774 {
4775 KIFONT::OUTLINE_FONT* outlineFont = static_cast<KIFONT::OUTLINE_FONT*>( font );
4776 PERMISSION permission = outlineFont->GetEmbeddingPermission();
4777
4778 if( permission == PERMISSION::EDITABLE || permission == PERMISSION::INSTALLABLE )
4779 fonts.insert( outlineFont );
4780 }
4781 }
4782 };
4783
4784 for( BOARD_ITEM* item : GraphicalItems() )
4785 processItem( item );
4786
4787 for( PCB_FIELD* field : GetFields() )
4788 processItem( field );
4789
4790 return fonts;
4791}
4792
4793
4795{
4796 for( KIFONT::OUTLINE_FONT* font : GetFonts() )
4797 {
4798 EMBEDDED_FILES::EMBEDDED_FILE* file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
4800 }
4801}
4802
4803
4805{
4806 m_componentClassCacheProxy->SetStaticComponentClass( aClass );
4807}
4808
4809
4811{
4812 return m_componentClassCacheProxy->GetStaticComponentClass();
4813}
4814
4815
4817{
4818 m_componentClassCacheProxy->RecomputeComponentClass();
4819}
4820
4821
4823{
4824 return m_componentClassCacheProxy->GetComponentClass();
4825}
4826
4827
4829{
4830 if( !m_componentClassCacheProxy->GetComponentClass()->IsEmpty() )
4831 return m_componentClassCacheProxy->GetComponentClass()->GetName();
4832
4833 return wxEmptyString;
4834}
4835
4836
4838 const std::unordered_set<wxString>& aComponentClassNames )
4839{
4840 const COMPONENT_CLASS* componentClass =
4841 aBoard->GetComponentClassManager().GetEffectiveStaticComponentClass( aComponentClassNames );
4842 SetStaticComponentClass( componentClass );
4843}
4844
4845
4847{
4848 m_componentClassCacheProxy->InvalidateCache();
4849}
4850
4851
4853{
4854 m_stackupMode = aMode;
4855
4857 {
4858 // Reset the stackup layers to the default values
4860 }
4861}
4862
4863
4865{
4866 wxCHECK2( m_stackupMode == FOOTPRINT_STACKUP::CUSTOM_LAYERS, /*void*/ );
4867
4869 m_stackupLayers = std::move( aLayers );
4870}
4871
4872
4874{
4875 if( !aBoard )
4876 return;
4877
4879 return;
4880
4881 const LSET boardCopper = LSET::AllCuMask( aBoard->GetCopperLayerCount() );
4882
4883 for( PAD* pad : Pads() )
4884 {
4885 if( pad->GetAttribute() == PAD_ATTRIB::PTH )
4886 {
4887 LSET padLayers = pad->GetLayerSet();
4888 padLayers |= boardCopper;
4889 pad->SetLayerSet( padLayers );
4890 }
4891 }
4892}
4893
4894
4895static struct FOOTPRINT_DESC
4896{
4898 {
4900
4901 if( zcMap.Choices().GetCount() == 0 )
4902 {
4904 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
4905 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
4906 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
4907 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
4908 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
4909 }
4910
4912
4913 if( layerEnum.Choices().GetCount() == 0 )
4914 {
4915 layerEnum.Undefined( UNDEFINED_LAYER );
4916
4917 for( PCB_LAYER_ID layer : LSET::AllLayersMask() )
4918 layerEnum.Map( layer, LSET::Name( layer ) );
4919 }
4920
4921 wxPGChoices fpLayers; // footprints might be placed only on F.Cu & B.Cu
4922 fpLayers.Add( LSET::Name( F_Cu ), F_Cu );
4923 fpLayers.Add( LSET::Name( B_Cu ), B_Cu );
4924
4931
4932 auto isNotFootprintHolder =
4933 []( INSPECTABLE* aItem ) -> bool
4934 {
4935 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aItem ) )
4936 {
4937 if( BOARD* board = footprint->GetBoard() )
4938 return !board->IsFootprintHolder();
4939 }
4940 return true;
4941 };
4942
4943 auto layer = new PROPERTY_ENUM<FOOTPRINT, PCB_LAYER_ID>( _HKI( "Layer" ),
4945 layer->SetChoices( fpLayers );
4946 layer->SetAvailableFunc( isNotFootprintHolder );
4947 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
4948
4949 propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Orientation" ),
4952 .SetAvailableFunc( isNotFootprintHolder );
4953
4954 const wxString groupFields = _HKI( "Fields" );
4955
4956 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Reference" ),
4958 groupFields );
4959
4960 const wxString propertyFields = _HKI( "Footprint Properties" );
4961
4962 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Link" ),
4964 propertyFields );
4965 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Description" ),
4967 propertyFields );
4968 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Keywords" ),
4970 propertyFields );
4971
4972 // Note: Also used by DRC engine
4973 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Component Class" ),
4975 propertyFields )
4977
4978 const wxString groupAttributes = _HKI( "Attributes" );
4979
4980 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Not in Schematic" ),
4981 &FOOTPRINT::SetBoardOnly, &FOOTPRINT::IsBoardOnly ), groupAttributes );
4982 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Position Files" ),
4984 groupAttributes );
4985 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Bill of Materials" ),
4987 groupAttributes );
4988 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Do not Populate" ),
4990 groupAttributes );
4991
4992 const wxString groupOverrides = _HKI( "Overrides" );
4993
4994 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exempt From Courtyard Requirement" ),
4996 groupOverrides );
4997 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>( _HKI( "Clearance Override" ),
5000 groupOverrides );
5001 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>( _HKI( "Solderpaste Margin Override" ),
5004 groupOverrides );
5005 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<double>>( _HKI( "Solderpaste Margin Ratio Override" ),
5009 groupOverrides );
5010 propMgr.AddProperty( new PROPERTY_ENUM<FOOTPRINT, ZONE_CONNECTION>( _HKI( "Zone Connection Style" ),
5012 groupOverrides );
5013 }
const char * name
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:44
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:27
virtual void ClearEditFlags()
Definition eda_item.h:163
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
const KIID m_Uuid
Definition eda_item.h:528
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:151
virtual bool Matches(const EDA_SEARCH_DATA &aSearchData, void *aAuxData) const
Compare the item against the search criteria in aSearchData.
Definition eda_item.h:413
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:332
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:153
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:263
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:55
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:560
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:962
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition pad.cpp:1354
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition pad.h:917
int GetDrillSizeY() const
Definition pad.h:321
PAD_ATTRIB GetAttribute() const
Definition pad.h:563
const wxString & GetNumber() const
Definition pad.h:137
VECTOR2I GetPosition() const override
Definition pad.h:209
int GetDrillSizeX() const
Definition pad.h:319
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:196
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1679
const PADSTACK & Padstack() const
Definition pad.h:333
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:420
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:437
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition pad.cpp:948
std::optional< int > GetLocalSolderMaskMargin() const
Definition pad.h:586
VECTOR2I GetSolderPasteMargin(PCB_LAYER_ID aLayer) const
Usually < 0 (mask shape smaller than pad)because the margin can be dependent on the pad size,...
Definition pad.cpp:1742
bool HasDrilledHole() const override
Definition pad.h:112
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition pad.cpp:1066
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition pad.h:264
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
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
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