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