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