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