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