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