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