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 (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26#include <magic_enum.hpp>
27
28#include <unordered_set>
29
30#include <bitmaps.h>
31#include <board.h>
33#include <confirm.h>
36#include <drc/drc_item.h>
37#include <embedded_files.h>
38#include <font/font.h>
39#include <font/outline_font.h>
40#include <footprint.h>
44#include <i18n_utility.h>
45#include <lset.h>
46#include <macros.h>
47#include <pad.h>
48#include <pcb_dimension.h>
49#include <pcb_edit_frame.h>
50#include <pcb_field.h>
51#include <pcb_group.h>
52#include <pcb_marker.h>
53#include <pcb_reference_image.h>
54#include <pcb_textbox.h>
55#include <pcb_track.h>
56#include <refdes_utils.h>
57#include <string_utils.h>
58#include <view/view.h>
59#include <zone.h>
60
61#include <google/protobuf/any.pb.h>
62#include <api/board/board_types.pb.h>
63#include <api/api_enums.h>
64#include <api/api_utils.h>
65#include <api/api_pcb_utils.h>
66#include <wx/log.h>
67
68
71 m_boundingBoxCacheTimeStamp( 0 ),
72 m_textExcludedBBoxCacheTimeStamp( 0 ),
73 m_hullCacheTimeStamp( 0 ),
74 m_initial_comments( nullptr ),
75 m_componentClass( nullptr )
76{
77 m_attributes = 0;
78 m_layer = F_Cu;
81 m_arflag = 0;
82 m_link = 0;
84 m_zoneConnection = ZONE_CONNECTION::INHERITED;
86 m_embedFonts = false;
87
88 // These are the mandatory fields for the editor to work
89 for( int i = 0; i < MANDATORY_FIELDS; i++ )
90 {
91 PCB_FIELD* field = new PCB_FIELD( this, i );
92 m_fields.push_back( field );
93
94 switch( i )
95 {
96 case REFERENCE_FIELD:
97 field->SetLayer( F_SilkS );
98 field->SetVisible( true );
99 break;
100 case VALUE_FIELD:
101 field->SetLayer( F_Fab );
102 field->SetVisible( true );
103 break;
104 default:
105 field->SetLayer( F_Fab );
106 field->SetVisible( false );
107 break;
108 }
109 }
110
111 m_3D_Drawings.clear();
112}
113
114
115FOOTPRINT::FOOTPRINT( const FOOTPRINT& aFootprint ) :
116 BOARD_ITEM_CONTAINER( aFootprint )
117{
118 m_pos = aFootprint.m_pos;
119 m_fpid = aFootprint.m_fpid;
120 m_attributes = aFootprint.m_attributes;
121 m_fpStatus = aFootprint.m_fpStatus;
122 m_orient = aFootprint.m_orient;
123 m_lastEditTime = aFootprint.m_lastEditTime;
124 m_link = aFootprint.m_link;
125 m_path = aFootprint.m_path;
126 m_embedFonts = aFootprint.m_embedFonts;
127
132 m_cachedHull = aFootprint.m_cachedHull;
134
135 m_clearance = aFootprint.m_clearance;
142
144
145 std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
146
147 // Copy fields
148 for( PCB_FIELD* field : aFootprint.Fields() )
149 {
150 PCB_FIELD* newField = static_cast<PCB_FIELD*>( field->Clone() );
151 ptrMap[field] = newField;
152 Add( newField, ADD_MODE::APPEND ); // Append to ensure indexes are identical
153 }
154
155 // Copy pads
156 for( PAD* pad : aFootprint.Pads() )
157 {
158 PAD* newPad = static_cast<PAD*>( pad->Clone() );
159 ptrMap[ pad ] = newPad;
160 Add( newPad, ADD_MODE::APPEND ); // Append to ensure indexes are identical
161 }
162
163 // Copy zones
164 for( ZONE* zone : aFootprint.Zones() )
165 {
166 ZONE* newZone = static_cast<ZONE*>( zone->Clone() );
167 ptrMap[ zone ] = newZone;
168 Add( newZone, ADD_MODE::APPEND ); // Append to ensure indexes are identical
169
170 // Ensure the net info is OK and especially uses the net info list
171 // living in the current board
172 // Needed when copying a fp from fp editor that has its own board
173 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
174 newZone->SetNetCode( -1 );
175 }
176
177 // Copy drawings
178 for( BOARD_ITEM* item : aFootprint.GraphicalItems() )
179 {
180 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
181 ptrMap[ item ] = newItem;
182 Add( newItem, ADD_MODE::APPEND ); // Append to ensure indexes are identical
183 }
184
185 // Copy groups
186 for( PCB_GROUP* group : aFootprint.Groups() )
187 {
188 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
189 ptrMap[ group ] = newGroup;
190 Add( newGroup, ADD_MODE::APPEND ); // Append to ensure indexes are identical
191 }
192
193 // Rebuild groups
194 for( PCB_GROUP* group : aFootprint.Groups() )
195 {
196 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( ptrMap[ group ] );
197
198 newGroup->GetItems().clear();
199
200 for( BOARD_ITEM* member : group->GetItems() )
201 {
202 if( ptrMap.count( member ) )
203 newGroup->AddItem( ptrMap[ member ] );
204 }
205 }
206
207 for( auto& [ name, file ] : aFootprint.EmbeddedFileMap() )
209
210 // Copy auxiliary data
211 m_3D_Drawings = aFootprint.m_3D_Drawings;
213 m_keywords = aFootprint.m_keywords;
214 m_privateLayers = aFootprint.m_privateLayers;
215
216 m_arflag = 0;
217
219 new wxArrayString( *aFootprint.m_initial_comments ) : nullptr;
220}
221
222
224 BOARD_ITEM_CONTAINER( aFootprint )
225{
226 *this = std::move( aFootprint );
227}
228
229
231{
232 // Untangle group parents before doing any deleting
233 for( PCB_GROUP* group : m_groups )
234 {
235 for( BOARD_ITEM* item : group->GetItems() )
236 item->SetParentGroup( nullptr );
237 }
238
239 // Clean up the owned elements
240 delete m_initial_comments;
241
242 for( PCB_FIELD* f : m_fields )
243 delete f;
244
245 m_fields.clear();
246
247 for( PAD* p : m_pads )
248 delete p;
249
250 m_pads.clear();
251
252 for( ZONE* zone : m_zones )
253 delete zone;
254
255 m_zones.clear();
256
257 for( PCB_GROUP* group : m_groups )
258 delete group;
259
260 m_groups.clear();
261
262 for( BOARD_ITEM* d : m_drawings )
263 delete d;
264
265 m_drawings.clear();
266
267 if( BOARD* board = GetBoard() )
268 board->IncrementTimeStamp();
269}
270
271
272void FOOTPRINT::Serialize( google::protobuf::Any &aContainer ) const
273{
274 using namespace kiapi::board;
275 types::FootprintInstance footprint;
276
277 footprint.mutable_id()->set_value( m_Uuid.AsStdString() );
278 footprint.mutable_position()->set_x_nm( GetPosition().x );
279 footprint.mutable_position()->set_y_nm( GetPosition().y );
280 footprint.mutable_orientation()->set_value_degrees( GetOrientationDegrees() );
281 footprint.set_layer( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( GetLayer() ) );
282 footprint.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
283 : kiapi::common::types::LockedState::LS_UNLOCKED );
284
285 google::protobuf::Any buf;
287 buf.UnpackTo( footprint.mutable_reference_field() );
288 GetField( VALUE_FIELD )->Serialize( buf );
289 buf.UnpackTo( footprint.mutable_value_field() );
291 buf.UnpackTo( footprint.mutable_datasheet_field() );
293 buf.UnpackTo( footprint.mutable_description_field() );
294
295 types::FootprintAttributes* attrs = footprint.mutable_attributes();
296
297 attrs->set_not_in_schematic( IsBoardOnly() );
298 attrs->set_exclude_from_position_files( IsExcludedFromPosFiles() );
299 attrs->set_exclude_from_bill_of_materials( IsExcludedFromBOM() );
300 attrs->set_exempt_from_courtyard_requirement( AllowMissingCourtyard() );
301 attrs->set_do_not_populate( IsDNP() );
302
303 types::Footprint* def = footprint.mutable_definition();
304
305 def->mutable_id()->CopyFrom( kiapi::common::LibIdToProto( GetFPID() ) );
306 // anchor?
307 def->mutable_attributes()->set_description( GetLibDescription().ToStdString() );
308 def->mutable_attributes()->set_keywords( GetKeywords().ToStdString() );
309
310 // TODO: serialize library mandatory fields
311
312 types::FootprintDesignRuleOverrides* overrides = def->mutable_overrides();
313
314 if( GetLocalClearance().has_value() )
315 overrides->mutable_copper_clearance()->set_value_nm( *GetLocalClearance() );
316
317 if( GetLocalSolderMaskMargin().has_value() )
318 overrides->mutable_solder_mask()->mutable_solder_mask_margin()->set_value_nm( *GetLocalSolderMaskMargin() );
319
320 if( GetLocalSolderPasteMargin().has_value() )
321 overrides->mutable_solder_paste()->mutable_solder_paste_margin()->set_value_nm( *GetLocalSolderPasteMargin() );
322
323 if( GetLocalSolderPasteMarginRatio().has_value() )
324 overrides->mutable_solder_paste()->mutable_solder_paste_margin_ratio()->set_value( *GetLocalSolderPasteMarginRatio() );
325
326 overrides->set_zone_connection(
327 ToProtoEnum<ZONE_CONNECTION, types::ZoneConnectionStyle>( GetLocalZoneConnection() ) );
328
329 for( const wxString& group : GetNetTiePadGroups() )
330 {
331 types::NetTieDefinition* netTie = def->add_net_ties();
332 wxStringTokenizer tokenizer( group, " " );
333
334 while( tokenizer.HasMoreTokens() )
335 netTie->add_pad_number( tokenizer.GetNextToken().ToStdString() );
336 }
337
338 for( PCB_LAYER_ID layer : GetPrivateLayers().Seq() )
339 def->add_private_layers( ToProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
340
341 for( const PCB_FIELD* item : Fields() )
342 {
343 if( item->GetId() < MANDATORY_FIELDS )
344 continue;
345
346 google::protobuf::Any* itemMsg = def->add_items();
347 item->Serialize( *itemMsg );
348 }
349
350 for( const PAD* item : Pads() )
351 {
352 google::protobuf::Any* itemMsg = def->add_items();
353 item->Serialize( *itemMsg );
354 }
355
356 for( const BOARD_ITEM* item : GraphicalItems() )
357 {
358 google::protobuf::Any* itemMsg = def->add_items();
359 item->Serialize( *itemMsg );
360 }
361
362 for( const ZONE* item : Zones() )
363 {
364 google::protobuf::Any* itemMsg = def->add_items();
365 item->Serialize( *itemMsg );
366 }
367
368 for( const FP_3DMODEL& model : Models() )
369 {
370 google::protobuf::Any* itemMsg = def->add_items();
371 types::Footprint3DModel modelMsg;
372 modelMsg.set_filename( model.m_Filename.ToUTF8() );
373 kiapi::common::PackVector3D( *modelMsg.mutable_scale(), model.m_Scale );
374 kiapi::common::PackVector3D( *modelMsg.mutable_rotation(), model.m_Rotation );
375 kiapi::common::PackVector3D( *modelMsg.mutable_offset(), model.m_Offset );
376 modelMsg.set_visible( model.m_Show );
377 modelMsg.set_opacity( model.m_Opacity );
378 itemMsg->PackFrom( modelMsg );
379 }
380
381 aContainer.PackFrom( footprint );
382}
383
384
385bool FOOTPRINT::Deserialize( const google::protobuf::Any &aContainer )
386{
387 using namespace kiapi::board;
388 types::FootprintInstance footprint;
389
390 if( !aContainer.UnpackTo( &footprint ) )
391 return false;
392
393 const_cast<KIID&>( m_Uuid ) = KIID( footprint.id().value() );
394 SetPosition( VECTOR2I( footprint.position().x_nm(), footprint.position().y_nm() ) );
395 SetOrientationDegrees( footprint.orientation().value_degrees() );
396 SetLayer( FromProtoEnum<PCB_LAYER_ID, types::BoardLayer>( footprint.layer() ) );
397 SetLocked( footprint.locked() == kiapi::common::types::LockedState::LS_LOCKED );
398
399 google::protobuf::Any buf;
400 types::Field mandatoryField;
401
402 if( footprint.has_reference_field() )
403 {
404 mandatoryField = footprint.reference_field();
405 mandatoryField.mutable_id()->set_id( REFERENCE_FIELD );
406 buf.PackFrom( mandatoryField );
408 }
409
410 if( footprint.has_value_field() )
411 {
412 mandatoryField = footprint.value_field();
413 mandatoryField.mutable_id()->set_id( VALUE_FIELD );
414 buf.PackFrom( mandatoryField );
416 }
417
418 if( footprint.has_datasheet_field() )
419 {
420 mandatoryField = footprint.datasheet_field();
421 mandatoryField.mutable_id()->set_id( DATASHEET_FIELD );
422 buf.PackFrom( mandatoryField );
424 }
425
426 if( footprint.has_description_field() )
427 {
428 mandatoryField = footprint.description_field();
429 mandatoryField.mutable_id()->set_id( DESCRIPTION_FIELD );
430 buf.PackFrom( mandatoryField );
432 }
433
434 SetBoardOnly( footprint.attributes().not_in_schematic() );
435 SetExcludedFromBOM( footprint.attributes().exclude_from_bill_of_materials() );
436 SetExcludedFromPosFiles( footprint.attributes().exclude_from_position_files() );
437 SetAllowMissingCourtyard( footprint.attributes().exempt_from_courtyard_requirement() );
438 SetDNP( footprint.attributes().do_not_populate() );
439
440 // Definition
441 SetFPID( kiapi::common::LibIdFromProto( footprint.definition().id() ) );
442 // TODO: how should anchor be handled?
443 SetLibDescription( footprint.definition().attributes().description() );
444 SetKeywords( footprint.definition().attributes().keywords() );
445
446 const types::FootprintDesignRuleOverrides& overrides = footprint.overrides();
447
448 if( overrides.has_copper_clearance() )
449 SetLocalClearance( overrides.copper_clearance().value_nm() );
450 else
451 SetLocalClearance( std::nullopt );
452
453 if( overrides.has_solder_mask() && overrides.solder_mask().has_solder_mask_margin() )
454 SetLocalSolderMaskMargin( overrides.solder_mask().solder_mask_margin().value_nm() );
455 else
456 SetLocalSolderMaskMargin( std::nullopt );
457
458 if( overrides.has_solder_paste() )
459 {
460 const types::SolderPasteOverrides& pasteSettings = overrides.solder_paste();
461
462 if( pasteSettings.has_solder_paste_margin() )
463 SetLocalSolderPasteMargin( pasteSettings.solder_paste_margin().value_nm() );
464 else
465 SetLocalSolderPasteMargin( std::nullopt );
466
467 if( pasteSettings.has_solder_paste_margin_ratio() )
468 SetLocalSolderPasteMarginRatio( pasteSettings.solder_paste_margin_ratio().value() );
469 else
470 SetLocalSolderPasteMarginRatio( std::nullopt );
471 }
472
473 SetLocalZoneConnection( FromProtoEnum<ZONE_CONNECTION>( overrides.zone_connection() ) );
474
475 for( const types::NetTieDefinition& netTieMsg : footprint.definition().net_ties() )
476 {
477 wxString group;
478
479 for( const std::string& pad : netTieMsg.pad_number() )
480 group.Append( wxString::Format( wxT( "%s " ), pad ) );
481
482 group.Trim();
484 }
485
486 LSET privateLayers;
487
488 for( int layerMsg : footprint.definition().private_layers() )
489 {
490 auto layer = static_cast<types::BoardLayer>( layerMsg );
491 privateLayers.set( FromProtoEnum<PCB_LAYER_ID, types::BoardLayer>( layer ) );
492 }
493
494 SetPrivateLayers( privateLayers );
495
496 // Footprint items
497 for( PCB_FIELD* field : Fields() )
498 {
499 if( field->GetId() >= MANDATORY_FIELDS )
500 Remove( field );
501 }
502
503 Pads().clear();
504 GraphicalItems().clear();
505 Zones().clear();
506 Groups().clear();
507 Models().clear();
508
509 for( const google::protobuf::Any& itemMsg : footprint.definition().items() )
510 {
511 std::optional<KICAD_T> type = kiapi::common::TypeNameFromAny( itemMsg );
512
513 if( !type )
514 {
515 // Bit of a hack here, but eventually 3D models should be promoted to a first-class
516 // object, at which point they can get their own serialization
517 if( itemMsg.type_url() == "type.googleapis.com/kiapi.board.types.Footprint3DModel" )
518 {
519 types::Footprint3DModel modelMsg;
520
521 if( !itemMsg.UnpackTo( &modelMsg ) )
522 continue;
523
524 FP_3DMODEL model;
525
526 model.m_Filename = wxString::FromUTF8( modelMsg.filename() );
527 model.m_Show = modelMsg.visible();
528 model.m_Opacity = modelMsg.opacity();
529 model.m_Scale = kiapi::common::UnpackVector3D( modelMsg.scale() );
530 model.m_Rotation = kiapi::common::UnpackVector3D( modelMsg.rotation() );
531 model.m_Offset = kiapi::common::UnpackVector3D( modelMsg.offset() );
532
533 Models().push_back( model );
534 }
535 else
536 {
537 wxLogTrace( traceApi, wxString::Format( wxS( "Attempting to unpack unknown type %s "
538 "from footprint message, skipping" ),
539 itemMsg.type_url() ) );
540 continue;
541 }
542 }
543
544 std::unique_ptr<BOARD_ITEM> item = CreateItemForType( *type, this );
545
546 if( item && item->Deserialize( itemMsg ) )
547 Add( item.release(), ADD_MODE::APPEND );
548 }
549
550 return true;
551}
552
553
555{
556 return m_fields[aFieldType];
557}
558
559
561{
562 return m_fields[aFieldType];
563}
564
565
567{
568 for( PCB_FIELD* field : m_fields )
569 {
570 if( field->GetId() == aFieldId )
571 return field;
572 }
573
574 return nullptr;
575}
576
577bool FOOTPRINT::HasFieldByName( const wxString& aFieldName ) const
578{
579 for( PCB_FIELD* field : m_fields )
580 {
581 if( field->GetCanonicalName() == aFieldName )
582 return true;
583 }
584
585 return false;
586}
587
588PCB_FIELD* FOOTPRINT::GetFieldByName( const wxString& aFieldName )
589{
590 if( aFieldName.empty() )
591 return nullptr;
592
593 for( PCB_FIELD* field : m_fields )
594 {
595 if( field->GetName() == aFieldName )
596 return field;
597 }
598
599 return nullptr;
600}
601
602
603wxString FOOTPRINT::GetFieldText( const wxString& aFieldName ) const
604{
605 for( const PCB_FIELD* field : m_fields )
606 {
607 if( aFieldName == field->GetName() || aFieldName == field->GetCanonicalName() )
608 return field->GetText();
609 }
610
611 return wxEmptyString;
612}
613
614
615void FOOTPRINT::GetFields( std::vector<PCB_FIELD*>& aVector, bool aVisibleOnly )
616{
617 for( PCB_FIELD* field : m_fields )
618 {
619 // FOOTPRINT field doesn't exist on FOOTPRINT, but it's kept in the m_fields vector
620 // for the moment so that the MANDATORY_FIELD ids line up with vector indices.
621 // This could be cleaned up by making m_fields a map in the future.
622 if( field->GetId() == FOOTPRINT_FIELD )
623 continue;
624
625 if( aVisibleOnly )
626 {
627 if( !field->IsVisible() || field->GetText().IsEmpty() )
628 continue;
629 }
630
631 aVector.push_back( field );
632 }
633}
634
635
637{
638 int newNdx = m_fields.size();
639
640 m_fields.push_back( new PCB_FIELD( aField ) );
641 return m_fields[newNdx];
642}
643
644
645void FOOTPRINT::RemoveField( const wxString& aFieldName )
646{
647 for( unsigned i = MANDATORY_FIELDS; i < m_fields.size(); ++i )
648 {
649 if( aFieldName == m_fields[i]->GetName( false ) )
650 {
651 m_fields.erase( m_fields.begin() + i );
652 return;
653 }
654 }
655}
656
657
658void FOOTPRINT::ApplyDefaultSettings( const BOARD& board, bool aStyleFields, bool aStyleText,
659 bool aStyleShapes )
660{
661 if( aStyleFields )
662 {
663 for( PCB_FIELD* field : m_fields )
664 field->StyleFromSettings( board.GetDesignSettings() );
665 }
666
667 for( BOARD_ITEM* item : m_drawings )
668 {
669 switch( item->Type() )
670 {
671 case PCB_TEXT_T:
672 case PCB_TEXTBOX_T:
673 if( aStyleText )
674 item->StyleFromSettings( board.GetDesignSettings() );
675
676 break;
677
678 case PCB_SHAPE_T:
679 if( aStyleShapes && !item->IsOnCopperLayer() )
680 item->StyleFromSettings( board.GetDesignSettings() );
681
682 break;
683
684 default:
685 break;
686 }
687 }
688}
689
690
692{
693 // replace null UUIDs if any by a valid uuid
694 std::vector< BOARD_ITEM* > item_list;
695
696 for( PCB_FIELD* field : m_fields )
697 item_list.push_back( field );
698
699 for( PAD* pad : m_pads )
700 item_list.push_back( pad );
701
702 for( BOARD_ITEM* gr_item : m_drawings )
703 item_list.push_back( gr_item );
704
705 // Note: one cannot fix null UUIDs inside the group, but it should not happen
706 // because null uuids can be found in old footprints, therefore without group
707 for( PCB_GROUP* group : m_groups )
708 item_list.push_back( group );
709
710 // Probably notneeded, because old fp do not have zones. But just in case.
711 for( ZONE* zone : m_zones )
712 item_list.push_back( zone );
713
714 bool changed = false;
715
716 for( BOARD_ITEM* item : item_list )
717 {
718 if( item->m_Uuid == niluuid )
719 {
720 const_cast<KIID&>( item->m_Uuid ) = KIID();
721 changed = true;
722 }
723 }
724
725 return changed;
726}
727
728
730{
731 BOARD_ITEM::operator=( aOther );
732
733 m_pos = aOther.m_pos;
734 m_fpid = aOther.m_fpid;
735 m_attributes = aOther.m_attributes;
736 m_fpStatus = aOther.m_fpStatus;
737 m_orient = aOther.m_orient;
738 m_lastEditTime = aOther.m_lastEditTime;
739 m_link = aOther.m_link;
740 m_path = aOther.m_path;
741
742 m_cachedBoundingBox = aOther.m_cachedBoundingBox;
743 m_boundingBoxCacheTimeStamp = aOther.m_boundingBoxCacheTimeStamp;
744 m_cachedTextExcludedBBox = aOther.m_cachedTextExcludedBBox;
745 m_textExcludedBBoxCacheTimeStamp = aOther.m_textExcludedBBoxCacheTimeStamp;
746 m_cachedHull = aOther.m_cachedHull;
747 m_hullCacheTimeStamp = aOther.m_hullCacheTimeStamp;
748
749 m_clearance = aOther.m_clearance;
750 m_solderMaskMargin = aOther.m_solderMaskMargin;
751 m_solderPasteMargin = aOther.m_solderPasteMargin;
752 m_solderPasteMarginRatio = aOther.m_solderPasteMarginRatio;
753 m_zoneConnection = aOther.m_zoneConnection;
754 m_netTiePadGroups = aOther.m_netTiePadGroups;
755
756 m_componentClass = aOther.m_componentClass;
757
758 // Move the fields
759 m_fields.clear();
760
761 for( PCB_FIELD* field : aOther.Fields() )
762 Add( field );
763
764 // Move the pads
765 m_pads.clear();
766
767 for( PAD* pad : aOther.Pads() )
768 Add( pad );
769
770 aOther.Pads().clear();
771
772 // Move the zones
773 m_zones.clear();
774
775 for( ZONE* item : aOther.Zones() )
776 {
777 Add( item );
778
779 // Ensure the net info is OK and especially uses the net info list
780 // living in the current board
781 // Needed when copying a fp from fp editor that has its own board
782 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
783 item->SetNetCode( -1 );
784 }
785
786 aOther.Zones().clear();
787
788 // Move the drawings
789 m_drawings.clear();
790
791 for( BOARD_ITEM* item : aOther.GraphicalItems() )
792 Add( item );
793
794 aOther.GraphicalItems().clear();
795
796 // Move the groups
797 m_groups.clear();
798
799 for( PCB_GROUP* group : aOther.Groups() )
800 Add( group );
801
802 aOther.Groups().clear();
803
804 // Copy auxiliary data
805 m_3D_Drawings = aOther.m_3D_Drawings;
806 m_libDescription = aOther.m_libDescription;
807 m_keywords = aOther.m_keywords;
808 m_privateLayers = aOther.m_privateLayers;
809
810 m_initial_comments = aOther.m_initial_comments;
811
812 // Clear the other item's containers since this is a move
813 aOther.Fields().clear();
814 aOther.Pads().clear();
815 aOther.Zones().clear();
816 aOther.GraphicalItems().clear();
817 aOther.m_initial_comments = nullptr;
818
819 return *this;
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;
833 m_link = aOther.m_link;
834 m_path = aOther.m_path;
835
840 m_cachedHull = aOther.m_cachedHull;
842
843 m_clearance = aOther.m_clearance;
849
851
852 std::map<BOARD_ITEM*, BOARD_ITEM*> ptrMap;
853
854 // Copy fields
855 m_fields.clear();
856
857 for( PCB_FIELD* field : aOther.GetFields() )
858 {
859 PCB_FIELD* newField = new PCB_FIELD( *field );
860 ptrMap[field] = newField;
861 Add( newField );
862 }
863
864 // Copy pads
865 m_pads.clear();
866
867 for( PAD* pad : aOther.Pads() )
868 {
869 PAD* newPad = new PAD( *pad );
870 ptrMap[ pad ] = newPad;
871 Add( newPad );
872 }
873
874 // Copy zones
875 m_zones.clear();
876
877 for( ZONE* zone : aOther.Zones() )
878 {
879 ZONE* newZone = static_cast<ZONE*>( zone->Clone() );
880 ptrMap[ zone ] = newZone;
881 Add( newZone );
882
883 // Ensure the net info is OK and especially uses the net info list
884 // living in the current board
885 // Needed when copying a fp from fp editor that has its own board
886 // Must be NETINFO_LIST::ORPHANED_ITEM for a keepout that has no net.
887 newZone->SetNetCode( -1 );
888 }
889
890 // Copy drawings
891 m_drawings.clear();
892
893 for( BOARD_ITEM* item : aOther.GraphicalItems() )
894 {
895 BOARD_ITEM* newItem = static_cast<BOARD_ITEM*>( item->Clone() );
896 ptrMap[ item ] = newItem;
897 Add( newItem );
898 }
899
900 // Copy groups
901 m_groups.clear();
902
903 for( PCB_GROUP* group : aOther.Groups() )
904 {
905 PCB_GROUP* newGroup = static_cast<PCB_GROUP*>( group->Clone() );
906 newGroup->GetItems().clear();
907
908 for( BOARD_ITEM* member : group->GetItems() )
909 newGroup->AddItem( ptrMap[ member ] );
910
911 Add( newGroup );
912 }
913
914 // Copy auxiliary data
917 m_keywords = aOther.m_keywords;
919
921 new wxArrayString( *aOther.m_initial_comments ) : nullptr;
922
923 return *this;
924}
925
926
928{
929 return HasFlag( COURTYARD_CONFLICT );
930}
931
932
933void FOOTPRINT::GetContextualTextVars( wxArrayString* aVars ) const
934{
935 aVars->push_back( wxT( "REFERENCE" ) );
936 aVars->push_back( wxT( "VALUE" ) );
937 aVars->push_back( wxT( "LAYER" ) );
938 aVars->push_back( wxT( "FOOTPRINT_LIBRARY" ) );
939 aVars->push_back( wxT( "FOOTPRINT_NAME" ) );
940 aVars->push_back( wxT( "SHORT_NET_NAME(<pad_number>)" ) );
941 aVars->push_back( wxT( "NET_NAME(<pad_number>)" ) );
942 aVars->push_back( wxT( "NET_CLASS(<pad_number>)" ) );
943 aVars->push_back( wxT( "PIN_NAME(<pad_number>)" ) );
944}
945
946
947bool FOOTPRINT::ResolveTextVar( wxString* token, int aDepth ) const
948{
949 if( GetBoard() && GetBoard()->GetBoardUse() == BOARD_USE::FPHOLDER )
950 return false;
951
952 if( token->IsSameAs( wxT( "REFERENCE" ) ) )
953 {
954 *token = Reference().GetShownText( false, aDepth + 1 );
955 return true;
956 }
957 else if( token->IsSameAs( wxT( "VALUE" ) ) )
958 {
959 *token = Value().GetShownText( false, aDepth + 1 );
960 return true;
961 }
962 else if( token->IsSameAs( wxT( "LAYER" ) ) )
963 {
964 *token = GetLayerName();
965 return true;
966 }
967 else if( token->IsSameAs( wxT( "FOOTPRINT_LIBRARY" ) ) )
968 {
970 return true;
971 }
972 else if( token->IsSameAs( wxT( "FOOTPRINT_NAME" ) ) )
973 {
975 return true;
976 }
977 else if( token->StartsWith( wxT( "SHORT_NET_NAME(" ) )
978 || token->StartsWith( wxT( "NET_NAME(" ) )
979 || token->StartsWith( wxT( "NET_CLASS(" ) )
980 || token->StartsWith( wxT( "PIN_NAME(" ) ) )
981 {
982 wxString padNumber = token->AfterFirst( '(' );
983 padNumber = padNumber.BeforeLast( ')' );
984
985 for( PAD* pad : Pads() )
986 {
987 if( pad->GetNumber() == padNumber )
988 {
989 if( token->StartsWith( wxT( "SHORT_NET_NAME" ) ) )
990 *token = pad->GetShortNetname();
991 else if( token->StartsWith( wxT( "NET_NAME" ) ) )
992 *token = pad->GetNetname();
993 else if( token->StartsWith( wxT( "NET_CLASS" ) ) )
994 *token = pad->GetNetClassVariableSubstitutionName();
995 else
996 *token = pad->GetPinFunction();
997
998 return true;
999 }
1000 }
1001 }
1002 else if( HasFieldByName( *token ) )
1003 {
1004 *token = GetFieldText( *token );
1005 return true;
1006 }
1007
1008 if( GetBoard() && GetBoard()->ResolveTextVar( token, aDepth + 1 ) )
1009 return true;
1010
1011 return false;
1012}
1013
1014
1016{
1017 // Force the ORPHANED dummy net info for all pads.
1018 // ORPHANED dummy net does not depend on a board
1019 for( PAD* pad : m_pads )
1020 pad->SetNetCode( NETINFO_LIST::ORPHANED );
1021}
1022
1023
1024void FOOTPRINT::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity )
1025{
1026 switch( aBoardItem->Type() )
1027 {
1028 case PCB_FIELD_T:
1029 // Always append fields
1030 m_fields.push_back( static_cast<PCB_FIELD*>( aBoardItem ) );
1031
1032 break;
1033
1034 case PCB_TEXT_T:
1035 case PCB_DIM_ALIGNED_T:
1036 case PCB_DIM_LEADER_T:
1037 case PCB_DIM_CENTER_T:
1038 case PCB_DIM_RADIAL_T:
1040 case PCB_SHAPE_T:
1041 case PCB_TEXTBOX_T:
1042 case PCB_TABLE_T:
1044 if( aMode == ADD_MODE::APPEND )
1045 m_drawings.push_back( aBoardItem );
1046 else
1047 m_drawings.push_front( aBoardItem );
1048 break;
1049
1050 case PCB_PAD_T:
1051 if( aMode == ADD_MODE::APPEND )
1052 m_pads.push_back( static_cast<PAD*>( aBoardItem ) );
1053 else
1054 m_pads.push_front( static_cast<PAD*>( aBoardItem ) );
1055 break;
1056
1057 case PCB_ZONE_T:
1058 if( aMode == ADD_MODE::APPEND )
1059 m_zones.push_back( static_cast<ZONE*>( aBoardItem ) );
1060 else
1061 m_zones.insert( m_zones.begin(), static_cast<ZONE*>( aBoardItem ) );
1062 break;
1063
1064 case PCB_GROUP_T:
1065 if( aMode == ADD_MODE::APPEND )
1066 m_groups.push_back( static_cast<PCB_GROUP*>( aBoardItem ) );
1067 else
1068 m_groups.insert( m_groups.begin(), static_cast<PCB_GROUP*>( aBoardItem ) );
1069 break;
1070
1071 default:
1072 {
1073 wxString msg;
1074 msg.Printf( wxT( "FOOTPRINT::Add() needs work: BOARD_ITEM type (%d) not handled" ),
1075 aBoardItem->Type() );
1076 wxFAIL_MSG( msg );
1077
1078 return;
1079 }
1080 }
1081
1082 aBoardItem->ClearEditFlags();
1083 aBoardItem->SetParent( this );
1084}
1085
1086
1087void FOOTPRINT::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aMode )
1088{
1089 switch( aBoardItem->Type() )
1090 {
1091 case PCB_FIELD_T:
1092 {
1093 for( auto it = m_fields.begin(); it != m_fields.end(); ++it )
1094 {
1095 if( *it == aBoardItem )
1096 {
1097 m_fields.erase( it );
1098 break;
1099 }
1100 }
1101 }
1102 break;
1103
1104 case PCB_TEXT_T:
1105 case PCB_DIM_ALIGNED_T:
1106 case PCB_DIM_CENTER_T:
1108 case PCB_DIM_RADIAL_T:
1109 case PCB_DIM_LEADER_T:
1110 case PCB_SHAPE_T:
1111 case PCB_TEXTBOX_T:
1112 case PCB_TABLE_T:
1114 for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
1115 {
1116 if( *it == aBoardItem )
1117 {
1118 m_drawings.erase( it );
1119 break;
1120 }
1121 }
1122
1123 break;
1124
1125 case PCB_PAD_T:
1126 for( auto it = m_pads.begin(); it != m_pads.end(); ++it )
1127 {
1128 if( *it == static_cast<PAD*>( aBoardItem ) )
1129 {
1130 m_pads.erase( it );
1131 break;
1132 }
1133 }
1134
1135 break;
1136
1137 case PCB_ZONE_T:
1138 for( auto it = m_zones.begin(); it != m_zones.end(); ++it )
1139 {
1140 if( *it == static_cast<ZONE*>( aBoardItem ) )
1141 {
1142 m_zones.erase( it );
1143 break;
1144 }
1145 }
1146
1147 break;
1148
1149 case PCB_GROUP_T:
1150 for( auto it = m_groups.begin(); it != m_groups.end(); ++it )
1151 {
1152 if( *it == static_cast<PCB_GROUP*>( aBoardItem ) )
1153 {
1154 m_groups.erase( it );
1155 break;
1156 }
1157 }
1158
1159 break;
1160
1161 default:
1162 {
1163 wxString msg;
1164 msg.Printf( wxT( "FOOTPRINT::Remove() needs work: BOARD_ITEM type (%d) not handled" ),
1165 aBoardItem->Type() );
1166 wxFAIL_MSG( msg );
1167 }
1168 }
1169
1170 aBoardItem->SetFlags( STRUCT_DELETED );
1171
1172 PCB_GROUP* parentGroup = aBoardItem->GetParentGroup();
1173
1174 if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
1175 parentGroup->RemoveItem( aBoardItem );
1176}
1177
1178
1179double FOOTPRINT::GetArea( int aPadding ) const
1180{
1181 BOX2I bbox = GetBoundingBox( false );
1182
1183 double w = std::abs( static_cast<double>( bbox.GetWidth() ) ) + aPadding;
1184 double h = std::abs( static_cast<double>( bbox.GetHeight() ) ) + aPadding;
1185 return w * h;
1186}
1187
1188
1190{
1191 int smd_count = 0;
1192 int tht_count = 0;
1193
1194 for( PAD* pad : m_pads )
1195 {
1196 switch( pad->GetProperty() )
1197 {
1198 case PAD_PROP::FIDUCIAL_GLBL:
1199 case PAD_PROP::FIDUCIAL_LOCAL:
1200 continue;
1201
1202 case PAD_PROP::HEATSINK:
1203 case PAD_PROP::CASTELLATED:
1204 case PAD_PROP::MECHANICAL:
1205 continue;
1206
1207 case PAD_PROP::NONE:
1208 case PAD_PROP::BGA:
1209 case PAD_PROP::TESTPOINT:
1210 break;
1211 }
1212
1213 switch( pad->GetAttribute() )
1214 {
1215 case PAD_ATTRIB::PTH:
1216 tht_count++;
1217 break;
1218
1219 case PAD_ATTRIB::SMD:
1220 if( pad->IsOnCopperLayer() )
1221 smd_count++;
1222
1223 break;
1224
1225 default:
1226 break;
1227 }
1228 }
1229
1230 // Footprints with plated through-hole pads should usually be marked through hole even if they
1231 // also have SMD because they might not be auto-placed. Exceptions to this might be shielded
1232 if( tht_count > 0 )
1233 return FP_THROUGH_HOLE;
1234
1235 if( smd_count > 0 )
1236 return FP_SMD;
1237
1238 return 0;
1239}
1240
1241
1243{
1244 if( ( m_attributes & FP_SMD ) == FP_SMD )
1245 return _( "SMD" );
1246
1248 return _( "Through hole" );
1249
1250 return _( "Other" );
1251}
1252
1253
1255{
1256 BOX2I bbox;
1257
1258 // We want the bounding box of the footprint pads at rot 0, not flipped
1259 // Create such a image:
1260 FOOTPRINT dummy( *this );
1261
1262 dummy.SetPosition( VECTOR2I( 0, 0 ) );
1263 dummy.SetOrientation( ANGLE_0 );
1264
1265 if( dummy.IsFlipped() )
1266 dummy.Flip( VECTOR2I( 0, 0 ), FLIP_DIRECTION::TOP_BOTTOM );
1267
1268 for( PAD* pad : dummy.Pads() )
1269 bbox.Merge( pad->GetBoundingBox() );
1270
1271 // Remove the parent and the group from the dummy footprint before deletion
1272 dummy.SetParent( nullptr );
1273 dummy.SetParentGroup( nullptr );
1274
1275 return bbox;
1276}
1277
1278
1280{
1281 for( BOARD_ITEM* item : m_drawings )
1282 {
1283 if( m_privateLayers.test( item->GetLayer() ) )
1284 continue;
1285
1286 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_TEXT_T )
1287 return false;
1288 }
1289
1290 return true;
1291}
1292
1293
1295{
1296 return GetBoundingBox( true );
1297}
1298
1299
1300const BOX2I FOOTPRINT::GetBoundingBox( bool aIncludeText ) const
1301{
1302 const BOARD* board = GetBoard();
1303
1304 if( board )
1305 {
1306 if( aIncludeText )
1307 {
1309 return m_cachedBoundingBox;
1310 }
1311 else
1312 {
1315 }
1316 }
1317
1318 std::vector<PCB_TEXT*> texts;
1319 bool isFPEdit = board && board->IsFootprintHolder();
1320
1321 BOX2I bbox( m_pos );
1322 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Give a min size to the bbox
1323
1324 // Calculate the footprint side
1325 PCB_LAYER_ID footprintSide = GetSide();
1326
1327 for( BOARD_ITEM* item : m_drawings )
1328 {
1329 if( m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
1330 continue;
1331
1332 // We want the bitmap bounding box just in the footprint editor
1333 // so it will start with the correct initial zoom
1334 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
1335 continue;
1336
1337 // Handle text separately
1338 if( item->Type() == PCB_TEXT_T )
1339 {
1340 texts.push_back( static_cast<PCB_TEXT*>( item ) );
1341 continue;
1342 }
1343
1344 // If we're not including text then drop annotations as well -- unless, of course, it's
1345 // an unsided footprint -- in which case it's likely to be nothing *but* annotations.
1346 if( !aIncludeText && footprintSide != UNDEFINED_LAYER )
1347 {
1348 if( BaseType( item->Type() ) == PCB_DIMENSION_T )
1349 continue;
1350
1351 if( item->GetLayer() == Cmts_User || item->GetLayer() == Dwgs_User
1352 || item->GetLayer() == Eco1_User || item->GetLayer() == Eco2_User )
1353 {
1354 continue;
1355 }
1356 }
1357
1358 bbox.Merge( item->GetBoundingBox() );
1359 }
1360
1361 for( PCB_FIELD* field : m_fields )
1362 {
1363 // Reference and value get their own processing
1364 if( !field->IsReference() && !field->IsValue() )
1365 texts.push_back( field );
1366 }
1367
1368 for( PAD* pad : m_pads )
1369 bbox.Merge( pad->GetBoundingBox() );
1370
1371 for( ZONE* zone : m_zones )
1372 bbox.Merge( zone->GetBoundingBox() );
1373
1374 bool noDrawItems = ( m_drawings.empty() && m_pads.empty() && m_zones.empty() );
1375
1376 // Groups do not contribute to the rect, only their members
1377 if( aIncludeText || noDrawItems )
1378 {
1379 // Only PCB_TEXT and PCB_FIELD items are independently selectable;
1380 // PCB_TEXTBOX items go in with other graphic items above.
1381 for( PCB_TEXT* text : texts )
1382 {
1383 if( !isFPEdit && m_privateLayers.test( text->GetLayer() ) )
1384 continue;
1385
1386 if( text->IsVisible() )
1387 bbox.Merge( text->GetBoundingBox() );
1388 }
1389
1390 // This can be further optimized when aIncludeInvisibleText is true, but currently
1391 // leaving this as is until it's determined there is a noticeable speed hit.
1392 bool valueLayerIsVisible = true;
1393 bool refLayerIsVisible = true;
1394
1395 if( board )
1396 {
1397 // The first "&&" conditional handles the user turning layers off as well as layers
1398 // not being present in the current PCB stackup. Values, references, and all
1399 // footprint text can also be turned off via the GAL meta-layers, so the 2nd and
1400 // 3rd "&&" conditionals handle that.
1401 valueLayerIsVisible = board->IsLayerVisible( Value().GetLayer() )
1403 && board->IsElementVisible( LAYER_FP_TEXT );
1404
1405 refLayerIsVisible = board->IsLayerVisible( Reference().GetLayer() )
1407 && board->IsElementVisible( LAYER_FP_TEXT );
1408 }
1409
1410
1411 if( ( Value().IsVisible() && valueLayerIsVisible ) || noDrawItems )
1412 {
1413 bbox.Merge( Value().GetBoundingBox() );
1414 }
1415
1416 if( ( Reference().IsVisible() && refLayerIsVisible ) || noDrawItems )
1417 {
1418 bbox.Merge( Reference().GetBoundingBox() );
1419 }
1420 }
1421
1422 if( board )
1423 {
1424 if( aIncludeText || noDrawItems )
1425 {
1427 m_cachedBoundingBox = bbox;
1428 }
1429 else
1430 {
1433 }
1434 }
1435
1436 return bbox;
1437}
1438
1439
1441{
1442 std::vector<PCB_TEXT*> texts;
1443 const BOARD* board = GetBoard();
1444 bool isFPEdit = board && board->IsFootprintHolder();
1445
1446 // Start with an uninitialized bounding box
1447 BOX2I bbox;
1448
1449 for( BOARD_ITEM* item : m_drawings )
1450 {
1451 if( m_privateLayers.test( item->GetLayer() ) && !isFPEdit )
1452 continue;
1453
1454 if( ( aLayers & item->GetLayerSet() ).none() )
1455 continue;
1456
1457 // We want the bitmap bounding box just in the footprint editor
1458 // so it will start with the correct initial zoom
1459 if( item->Type() == PCB_REFERENCE_IMAGE_T && !isFPEdit )
1460 continue;
1461
1462 bbox.Merge( item->GetBoundingBox() );
1463 }
1464
1465 for( PAD* pad : m_pads )
1466 {
1467 if( ( aLayers & pad->GetLayerSet() ).none() )
1468 continue;
1469
1470 bbox.Merge( pad->GetBoundingBox() );
1471 }
1472
1473 for( ZONE* zone : m_zones )
1474 {
1475 if( ( aLayers & zone->GetLayerSet() ).none() )
1476 continue;
1477
1478 bbox.Merge( zone->GetBoundingBox() );
1479 }
1480
1481 return bbox;
1482}
1483
1484
1486{
1487 const BOARD* board = GetBoard();
1488 bool isFPEdit = board && board->IsFootprintHolder();
1489
1490 if( board )
1491 {
1492 if( m_hullCacheTimeStamp >= board->GetTimeStamp() )
1493 return m_cachedHull;
1494 }
1495
1496 SHAPE_POLY_SET rawPolys;
1497 SHAPE_POLY_SET hull;
1498
1499 for( BOARD_ITEM* item : m_drawings )
1500 {
1501 if( !isFPEdit && m_privateLayers.test( item->GetLayer() ) )
1502 continue;
1503
1504 if( item->Type() != PCB_FIELD_T && item->Type() != PCB_REFERENCE_IMAGE_T )
1505 {
1506 item->TransformShapeToPolygon( rawPolys, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
1507 ERROR_OUTSIDE );
1508 }
1509
1510 // We intentionally exclude footprint fields from the bounding hull.
1511 }
1512
1513 for( PAD* pad : m_pads )
1514 {
1515 pad->Padstack().ForEachUniqueLayer(
1516 [&]( PCB_LAYER_ID aLayer )
1517 {
1518 pad->TransformShapeToPolygon( rawPolys, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
1519 } );
1520
1521 // In case hole is larger than pad
1522 pad->TransformHoleToPolygon( rawPolys, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
1523 }
1524
1525 for( ZONE* zone : m_zones )
1526 {
1527 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
1528 {
1529 const SHAPE_POLY_SET& layerPoly = *zone->GetFilledPolysList( layer );
1530
1531 for( int ii = 0; ii < layerPoly.OutlineCount(); ii++ )
1532 {
1533 const SHAPE_LINE_CHAIN& poly = layerPoly.COutline( ii );
1534 rawPolys.AddOutline( poly );
1535 }
1536 }
1537 }
1538
1539 // If there are some graphic items, build the actual hull.
1540 // However if no items, create a minimal polygon (can happen if a footprint
1541 // is created with no item: it contains only 2 texts.
1542 if( rawPolys.OutlineCount() == 0 )
1543 {
1544 // generate a small dummy rectangular outline around the anchor
1545 const int halfsize = pcbIUScale.mmToIU( 1.0 );
1546
1547 rawPolys.NewOutline();
1548
1549 // add a square:
1550 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y - halfsize );
1551 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y - halfsize );
1552 rawPolys.Append( GetPosition().x + halfsize, GetPosition().y + halfsize );
1553 rawPolys.Append( GetPosition().x - halfsize, GetPosition().y + halfsize );
1554 }
1555
1556 std::vector<VECTOR2I> convex_hull;
1557 BuildConvexHull( convex_hull, rawPolys );
1558
1561
1562 for( const VECTOR2I& pt : convex_hull )
1563 m_cachedHull.Append( pt );
1564
1565 if( board )
1567
1568 return m_cachedHull;
1569}
1570
1571
1572void FOOTPRINT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1573{
1574 wxString msg, msg2;
1575
1576 // Don't use GetShownText(); we want to see the variable references here
1577 aList.emplace_back( UnescapeString( Reference().GetText() ),
1578 UnescapeString( Value().GetText() ) );
1579
1580 if( aFrame->IsType( FRAME_FOOTPRINT_VIEWER )
1581 || aFrame->IsType( FRAME_FOOTPRINT_CHOOSER )
1582 || aFrame->IsType( FRAME_FOOTPRINT_EDITOR ) )
1583 {
1584 size_t padCount = GetPadCount( DO_NOT_INCLUDE_NPTH );
1585
1586 aList.emplace_back( _( "Library" ), GetFPID().GetLibNickname().wx_str() );
1587
1588 aList.emplace_back( _( "Footprint Name" ), GetFPID().GetLibItemName().wx_str() );
1589
1590 aList.emplace_back( _( "Pads" ), wxString::Format( wxT( "%zu" ), padCount ) );
1591
1592 aList.emplace_back( wxString::Format( _( "Doc: %s" ), GetLibDescription() ),
1593 wxString::Format( _( "Keywords: %s" ), GetKeywords() ) );
1594
1595 return;
1596 }
1597
1598 // aFrame is the board editor:
1599
1600 switch( GetSide() )
1601 {
1602 case F_Cu: aList.emplace_back( _( "Board Side" ), _( "Front" ) ); break;
1603 case B_Cu: aList.emplace_back( _( "Board Side" ), _( "Back (Flipped)" ) ); break;
1604 default: /* unsided: user-layers only, etc. */ break;
1605 }
1606
1607 auto addToken = []( wxString* aStr, const wxString& aAttr )
1608 {
1609 if( !aStr->IsEmpty() )
1610 *aStr += wxT( ", " );
1611
1612 *aStr += aAttr;
1613 };
1614
1615 wxString status;
1616 wxString attrs;
1617
1618 if( IsLocked() )
1619 addToken( &status, _( "Locked" ) );
1620
1621 if( m_fpStatus & FP_is_PLACED )
1622 addToken( &status, _( "autoplaced" ) );
1623
1625 addToken( &attrs, _( "not in schematic" ) );
1626
1628 addToken( &attrs, _( "exclude from pos files" ) );
1629
1631 addToken( &attrs, _( "exclude from BOM" ) );
1632
1633 if( m_attributes & FP_DNP )
1634 addToken( &attrs, _( "DNP" ) );
1635
1636 aList.emplace_back( _( "Status: " ) + status, _( "Attributes:" ) + wxS( " " ) + attrs );
1637
1638 aList.emplace_back( _( "Rotation" ), wxString::Format( wxT( "%.4g" ),
1639 GetOrientation().AsDegrees() ) );
1640
1641 if( m_componentClass )
1642 {
1643 aList.emplace_back( _( "Component Class" ), m_componentClass->GetName() );
1644 }
1645
1646 msg.Printf( _( "Footprint: %s" ), m_fpid.GetUniStringLibId() );
1647 msg2.Printf( _( "3D-Shape: %s" ), m_3D_Drawings.empty() ? _( "<none>" )
1648 : m_3D_Drawings.front().m_Filename );
1649 aList.emplace_back( msg, msg2 );
1650
1651 msg.Printf( _( "Doc: %s" ), m_libDescription );
1652 msg2.Printf( _( "Keywords: %s" ), m_keywords );
1653 aList.emplace_back( msg, msg2 );
1654}
1655
1656
1658{
1659 if( const BOARD* board = GetBoard() )
1660 {
1661 if( board->IsFootprintHolder() )
1662 return UNDEFINED_LAYER;
1663 }
1664
1665 // Test pads first; they're the most likely to return a quick answer.
1666 for( PAD* pad : m_pads )
1667 {
1668 if( ( LSET::SideSpecificMask() & pad->GetLayerSet() ).any() )
1669 return GetLayer();
1670 }
1671
1672 for( BOARD_ITEM* item : m_drawings )
1673 {
1674 if( LSET::SideSpecificMask().test( item->GetLayer() ) )
1675 return GetLayer();
1676 }
1677
1678 for( ZONE* zone : m_zones )
1679 {
1680 if( ( LSET::SideSpecificMask() & zone->GetLayerSet() ).any() )
1681 return GetLayer();
1682 }
1683
1684 return UNDEFINED_LAYER;
1685}
1686
1687
1689{
1690 // If we have any pads, fall back on normal checking
1691 for( PAD* pad : m_pads )
1692 {
1693 if( pad->IsOnLayer( aLayer ) )
1694 return true;
1695 }
1696
1697 for( ZONE* zone : m_zones )
1698 {
1699 if( zone->IsOnLayer( aLayer ) )
1700 return true;
1701 }
1702
1703 for( PCB_FIELD* field : m_fields )
1704 {
1705 if( field->IsOnLayer( aLayer ) )
1706 return true;
1707 }
1708
1709 for( BOARD_ITEM* item : m_drawings )
1710 {
1711 if( item->IsOnLayer( aLayer ) )
1712 return true;
1713 }
1714
1715 return false;
1716}
1717
1718
1719bool FOOTPRINT::HitTestOnLayer( const VECTOR2I& aPosition, PCB_LAYER_ID aLayer, int aAccuracy ) const
1720{
1721 for( PAD* pad : m_pads )
1722 {
1723 if( pad->IsOnLayer( aLayer ) && pad->HitTest( aPosition, aAccuracy ) )
1724 return true;
1725 }
1726
1727 for( ZONE* zone : m_zones )
1728 {
1729 if( zone->IsOnLayer( aLayer ) && zone->HitTest( aPosition, aAccuracy ) )
1730 return true;
1731 }
1732
1733 for( BOARD_ITEM* item : m_drawings )
1734 {
1735 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer )
1736 && item->HitTest( aPosition, aAccuracy ) )
1737 {
1738 return true;
1739 }
1740 }
1741
1742 return false;
1743}
1744
1745
1746bool FOOTPRINT::HitTestOnLayer( const BOX2I& aRect, bool aContained, PCB_LAYER_ID aLayer, int aAccuracy ) const
1747{
1748 std::vector<BOARD_ITEM*> items;
1749
1750 for( PAD* pad : m_pads )
1751 {
1752 if( pad->IsOnLayer( aLayer ) )
1753 items.push_back( pad );
1754 }
1755
1756 for( ZONE* zone : m_zones )
1757 {
1758 if( zone->IsOnLayer( aLayer ) )
1759 items.push_back( zone );
1760 }
1761
1762 for( BOARD_ITEM* item : m_drawings )
1763 {
1764 if( item->Type() != PCB_TEXT_T && item->IsOnLayer( aLayer ) )
1765 items.push_back( item );
1766 }
1767
1768 // If we require the elements to be contained in the rect and any of them are not,
1769 // we can return false;
1770 // Conversely, if we just require any of the elements to have a hit, we can return true
1771 // when the first one is found.
1772 for( BOARD_ITEM* item : items )
1773 {
1774 if( !aContained && item->HitTest( aRect, aContained, aAccuracy ) )
1775 return true;
1776 else if( aContained && !item->HitTest( aRect, aContained, aAccuracy ) )
1777 return false;
1778 }
1779
1780 // If we didn't exit in the loop, that means that we did not return false for aContained or
1781 // we did not return true for !aContained. So we can just return the bool with a test of
1782 // whether there were any elements or not.
1783 return !items.empty() && aContained;
1784}
1785
1786
1787bool FOOTPRINT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1788{
1789 BOX2I rect = GetBoundingBox( false );
1790 return rect.Inflate( aAccuracy ).Contains( aPosition );
1791}
1792
1793
1794bool FOOTPRINT::HitTestAccurate( const VECTOR2I& aPosition, int aAccuracy ) const
1795{
1796 return GetBoundingHull().Collide( aPosition, aAccuracy );
1797}
1798
1799
1800bool FOOTPRINT::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1801{
1802 BOX2I arect = aRect;
1803 arect.Inflate( aAccuracy );
1804
1805 if( aContained )
1806 {
1807 return arect.Contains( GetBoundingBox( false ) );
1808 }
1809 else
1810 {
1811 // If the rect does not intersect the bounding box, skip any tests
1812 if( !aRect.Intersects( GetBoundingBox( false ) ) )
1813 return false;
1814
1815 // If there are no pads, zones, or drawings, allow intersection with text
1816 if( m_pads.empty() && m_zones.empty() && m_drawings.empty() )
1817 return GetBoundingBox( true ).Intersects( arect );
1818
1819 // Determine if any elements in the FOOTPRINT intersect the rect
1820 for( PAD* pad : m_pads )
1821 {
1822 if( pad->HitTest( arect, false, 0 ) )
1823 return true;
1824 }
1825
1826 for( ZONE* zone : m_zones )
1827 {
1828 if( zone->HitTest( arect, false, 0 ) )
1829 return true;
1830 }
1831
1832 // PCB fields are selectable on their own, so they don't get tested
1833
1834 for( BOARD_ITEM* item : m_drawings )
1835 {
1836 // Text items are selectable on their own, and are therefore excluded from this
1837 // test. TextBox items are NOT selectable on their own, and so MUST be included
1838 // here. Bitmaps aren't selectable since they aren't displayed.
1839 if( item->Type() != PCB_TEXT_T && item->HitTest( arect, false, 0 ) )
1840 return true;
1841 }
1842
1843 // Groups are not hit-tested; only their members
1844
1845 // No items were hit
1846 return false;
1847 }
1848}
1849
1850
1851PAD* FOOTPRINT::FindPadByNumber( const wxString& aPadNumber, PAD* aSearchAfterMe ) const
1852{
1853 bool can_select = aSearchAfterMe ? false : true;
1854
1855 for( PAD* pad : m_pads )
1856 {
1857 if( !can_select && pad == aSearchAfterMe )
1858 {
1859 can_select = true;
1860 continue;
1861 }
1862
1863 if( can_select && pad->GetNumber() == aPadNumber )
1864 return pad;
1865 }
1866
1867 return nullptr;
1868}
1869
1870
1871PAD* FOOTPRINT::GetPad( const VECTOR2I& aPosition, LSET aLayerMask )
1872{
1873 for( PAD* pad : m_pads )
1874 {
1875 // ... and on the correct layer.
1876 if( !( pad->GetLayerSet() & aLayerMask ).any() )
1877 continue;
1878
1879 if( pad->HitTest( aPosition ) )
1880 return pad;
1881 }
1882
1883 return nullptr;
1884}
1885
1886
1887std::vector<const PAD*> FOOTPRINT::GetPads( const wxString& aPadNumber, const PAD* aIgnore ) const
1888{
1889 std::vector<const PAD*> retv;
1890
1891 for( const PAD* pad : m_pads )
1892 {
1893 if( ( aIgnore && aIgnore == pad ) || ( pad->GetNumber() != aPadNumber ) )
1894 continue;
1895
1896 retv.push_back( pad );
1897 }
1898
1899 return retv;
1900}
1901
1902
1903unsigned FOOTPRINT::GetPadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
1904{
1905 if( aIncludeNPTH )
1906 return m_pads.size();
1907
1908 unsigned cnt = 0;
1909
1910 for( PAD* pad : m_pads )
1911 {
1912 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1913 continue;
1914
1915 cnt++;
1916 }
1917
1918 return cnt;
1919}
1920
1921
1922std::set<wxString> FOOTPRINT::GetUniquePadNumbers( INCLUDE_NPTH_T aIncludeNPTH ) const
1923{
1924 std::set<wxString> usedNumbers;
1925
1926 // Create a set of used pad numbers
1927 for( PAD* pad : m_pads )
1928 {
1929 // Skip pads not on copper layers (used to build complex
1930 // solder paste shapes for instance)
1931 if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
1932 continue;
1933
1934 // Skip pads with no name, because they are usually "mechanical"
1935 // pads, not "electrical" pads
1936 if( pad->GetNumber().IsEmpty() )
1937 continue;
1938
1939 if( !aIncludeNPTH )
1940 {
1941 // skip NPTH
1942 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1943 continue;
1944 }
1945
1946 usedNumbers.insert( pad->GetNumber() );
1947 }
1948
1949 return usedNumbers;
1950}
1951
1952
1953unsigned FOOTPRINT::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
1954{
1955 return GetUniquePadNumbers( aIncludeNPTH ).size();
1956}
1957
1958
1960{
1961 if( nullptr == a3DModel )
1962 return;
1963
1964 if( !a3DModel->m_Filename.empty() )
1965 m_3D_Drawings.push_back( *a3DModel );
1966}
1967
1968
1969// see footprint.h
1970INSPECT_RESULT FOOTPRINT::Visit( INSPECTOR inspector, void* testData,
1971 const std::vector<KICAD_T>& aScanTypes )
1972{
1973#if 0 && defined(DEBUG)
1974 std::cout << GetClass().mb_str() << ' ';
1975#endif
1976
1977 bool drawingsScanned = false;
1978
1979 for( KICAD_T scanType : aScanTypes )
1980 {
1981 switch( scanType )
1982 {
1983 case PCB_FOOTPRINT_T:
1984 if( inspector( this, testData ) == INSPECT_RESULT::QUIT )
1985 return INSPECT_RESULT::QUIT;
1986
1987 break;
1988
1989 case PCB_PAD_T:
1990 if( IterateForward<PAD*>( m_pads, inspector, testData, { scanType } )
1991 == INSPECT_RESULT::QUIT )
1992 {
1993 return INSPECT_RESULT::QUIT;
1994 }
1995
1996 break;
1997
1998 case PCB_ZONE_T:
1999 if( IterateForward<ZONE*>( m_zones, inspector, testData, { scanType } )
2000 == INSPECT_RESULT::QUIT )
2001 {
2002 return INSPECT_RESULT::QUIT;
2003 }
2004
2005 break;
2006
2007 case PCB_FIELD_T:
2008 if( IterateForward<PCB_FIELD*>( m_fields, inspector, testData, { scanType } )
2009 == INSPECT_RESULT::QUIT )
2010 {
2011 return INSPECT_RESULT::QUIT;
2012 }
2013
2014 break;
2015
2016 case PCB_TEXT_T:
2017 case PCB_DIM_ALIGNED_T:
2018 case PCB_DIM_LEADER_T:
2019 case PCB_DIM_CENTER_T:
2020 case PCB_DIM_RADIAL_T:
2022 case PCB_SHAPE_T:
2023 case PCB_TEXTBOX_T:
2024 case PCB_TABLE_T:
2025 case PCB_TABLECELL_T:
2026 if( !drawingsScanned )
2027 {
2028 if( IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, aScanTypes )
2029 == INSPECT_RESULT::QUIT )
2030 {
2031 return INSPECT_RESULT::QUIT;
2032 }
2033
2034 drawingsScanned = true;
2035 }
2036
2037 break;
2038
2039 case PCB_GROUP_T:
2040 if( IterateForward<PCB_GROUP*>( m_groups, inspector, testData, { scanType } )
2041 == INSPECT_RESULT::QUIT )
2042 {
2043 return INSPECT_RESULT::QUIT;
2044 }
2045
2046 break;
2047
2048 default:
2049 break;
2050 }
2051 }
2052
2053 return INSPECT_RESULT::CONTINUE;
2054}
2055
2056
2057wxString FOOTPRINT::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
2058{
2059 wxString reference = GetReference();
2060
2061 if( reference.IsEmpty() )
2062 reference = _( "<no reference designator>" );
2063
2064 return wxString::Format( _( "Footprint %s" ), reference );
2065}
2066
2067
2069{
2070 return BITMAPS::module;
2071}
2072
2073
2075{
2076 return new FOOTPRINT( *this );
2077}
2078
2079
2080void FOOTPRINT::RunOnChildren( const std::function<void ( BOARD_ITEM* )>& aFunction ) const
2081{
2082 try
2083 {
2084 for( PCB_FIELD* field : m_fields )
2085 aFunction( field );
2086
2087 for( PAD* pad : m_pads )
2088 aFunction( pad );
2089
2090 for( ZONE* zone : m_zones )
2091 aFunction( zone );
2092
2093 for( PCB_GROUP* group : m_groups )
2094 aFunction( group );
2095
2096 for( BOARD_ITEM* drawing : m_drawings )
2097 aFunction( drawing );
2098 }
2099 catch( std::bad_function_call& )
2100 {
2101 wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnChildren" ) );
2102 }
2103}
2104
2105
2106void FOOTPRINT::RunOnDescendants( const std::function<void( BOARD_ITEM* )>& aFunction,
2107 int aDepth ) const
2108{
2109 // Avoid freezes with infinite recursion
2110 if( aDepth > 20 )
2111 return;
2112
2113 try
2114 {
2115 for( PCB_FIELD* field : m_fields )
2116 aFunction( field );
2117
2118 for( PAD* pad : m_pads )
2119 aFunction( pad );
2120
2121 for( ZONE* zone : m_zones )
2122 aFunction( zone );
2123
2124 for( PCB_GROUP* group : m_groups )
2125 {
2126 aFunction( group );
2127 group->RunOnDescendants( aFunction, aDepth + 1 );
2128 }
2129
2130 for( BOARD_ITEM* drawing : m_drawings )
2131 {
2132 aFunction( drawing );
2133 drawing->RunOnDescendants( aFunction, aDepth + 1 );
2134 }
2135 }
2136 catch( std::bad_function_call& )
2137 {
2138 wxFAIL_MSG( wxT( "Error running FOOTPRINT::RunOnDescendants" ) );
2139 }
2140}
2141
2142
2143std::vector<int> FOOTPRINT::ViewGetLayers() const
2144{
2145 std::vector<int> layers;
2146
2147 layers.reserve( 6 );
2148 layers.push_back( LAYER_ANCHOR );
2149
2150 switch( m_layer )
2151 {
2152 default:
2153 wxASSERT_MSG( false, wxT( "Illegal layer" ) ); // do you really have footprints placed
2154 // on other layers?
2156
2157 case F_Cu:
2158 layers.push_back( LAYER_FOOTPRINTS_FR );
2159 break;
2160
2161 case B_Cu:
2162 layers.push_back( LAYER_FOOTPRINTS_BK );
2163 break;
2164 }
2165
2166 if( IsLocked() )
2167 layers.push_back( LAYER_LOCKED_ITEM_SHADOW );
2168
2169 if( IsConflicting() )
2170 layers.push_back( LAYER_CONFLICTS_SHADOW );
2171
2172 // If there are no pads, and only drawings on a silkscreen layer, then report the silkscreen
2173 // layer as well so that the component can be edited with the silkscreen layer
2174 bool f_silk = false, b_silk = false, non_silk = false;
2175
2176 for( BOARD_ITEM* item : m_drawings )
2177 {
2178 if( item->GetLayer() == F_SilkS )
2179 f_silk = true;
2180 else if( item->GetLayer() == B_SilkS )
2181 b_silk = true;
2182 else
2183 non_silk = true;
2184 }
2185
2186 if( ( f_silk || b_silk ) && !non_silk && m_pads.empty() )
2187 {
2188 if( f_silk )
2189 layers.push_back( F_SilkS );
2190
2191 if( b_silk )
2192 layers.push_back( B_SilkS );
2193 }
2194
2195 return layers;
2196}
2197
2198
2199double FOOTPRINT::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
2200{
2201 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2202 {
2203 // The locked shadow shape is shown only if the footprint itself is visible
2204 if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
2205 return 0.0;
2206
2207 if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
2208 return 0.0;
2209
2210 return std::numeric_limits<double>::max();
2211 }
2212
2213 if( aLayer == LAYER_CONFLICTS_SHADOW && IsConflicting() )
2214 {
2215 // The locked shadow shape is shown only if the footprint itself is visible
2216 if( ( m_layer == F_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_FR ) )
2217 return 0.0;
2218
2219 if( ( m_layer == B_Cu ) && aView->IsLayerVisible( LAYER_FOOTPRINTS_BK ) )
2220 return 0.0;
2221
2222 return std::numeric_limits<double>::max();
2223 }
2224
2225 int layer = ( m_layer == F_Cu ) ? LAYER_FOOTPRINTS_FR :
2227
2228 // Currently this is only pertinent for the anchor layer; everything else is drawn from the
2229 // children.
2230 // The "good" value is experimentally chosen.
2231 #define MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY 1.5
2232
2233 if( aView->IsLayerVisible( layer ) )
2235
2236 return std::numeric_limits<double>::max();
2237}
2238
2239
2241{
2242 BOX2I area = GetBoundingBox( true );
2243
2244 // Inflate in case clearance lines are drawn around pads, etc.
2245 if( const BOARD* board = GetBoard() )
2246 {
2247 int biggest_clearance = board->GetMaxClearanceValue();
2248 area.Inflate( biggest_clearance );
2249 }
2250
2251 return area;
2252}
2253
2254
2255bool FOOTPRINT::IsLibNameValid( const wxString & aName )
2256{
2257 const wxChar * invalids = StringLibNameInvalidChars( false );
2258
2259 if( aName.find_first_of( invalids ) != std::string::npos )
2260 return false;
2261
2262 return true;
2263}
2264
2265
2266const wxChar* FOOTPRINT::StringLibNameInvalidChars( bool aUserReadable )
2267{
2268 // This list of characters is also duplicated in validators.cpp and
2269 // lib_id.cpp
2270 // TODO: Unify forbidden character lists - Warning, invalid filename characters are not the same
2271 // as invalid LIB_ID characters. We will need to separate the FP filenames from FP names before this
2272 // can be unified
2273 static const wxChar invalidChars[] = wxT("%$<>\t\n\r\"\\/:");
2274 static const wxChar invalidCharsReadable[] = wxT("% $ < > 'tab' 'return' 'line feed' \\ \" / :");
2275
2276 if( aUserReadable )
2277 return invalidCharsReadable;
2278 else
2279 return invalidChars;
2280}
2281
2282
2283void FOOTPRINT::Move( const VECTOR2I& aMoveVector )
2284{
2285 if( aMoveVector.x == 0 && aMoveVector.y == 0 )
2286 return;
2287
2288 VECTOR2I newpos = m_pos + aMoveVector;
2289 SetPosition( newpos );
2290}
2291
2292
2293void FOOTPRINT::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
2294{
2295 if( aAngle == ANGLE_0 )
2296 return;
2297
2298 EDA_ANGLE orientation = GetOrientation();
2299 EDA_ANGLE newOrientation = orientation + aAngle;
2300 VECTOR2I newpos = m_pos;
2301 RotatePoint( newpos, aRotCentre, aAngle );
2302 SetPosition( newpos );
2303 SetOrientation( newOrientation );
2304
2305 for( PCB_FIELD* field : m_fields )
2306 field->KeepUpright();
2307
2308 for( BOARD_ITEM* item : m_drawings )
2309 {
2310 if( item->Type() == PCB_TEXT_T )
2311 static_cast<PCB_TEXT*>( item )->KeepUpright();
2312 }
2313
2317}
2318
2319
2321{
2322 wxASSERT( aLayer == F_Cu || aLayer == B_Cu );
2323
2324 if( aLayer != GetLayer() )
2325 Flip( GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
2326}
2327
2328
2329void FOOTPRINT::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
2330{
2331 // Move footprint to its final position:
2332 VECTOR2I finalPos = m_pos;
2333
2334 // Now Flip the footprint.
2335 // Flipping a footprint is a specific transform: it is not mirrored like a text.
2336 // We have to change the side, and ensure the footprint rotation is modified according to the
2337 // transform, because this parameter is used in pick and place files, and when updating the
2338 // footprint from library.
2339 // When flipped around the X axis (Y coordinates changed) orientation is negated
2340 // When flipped around the Y axis (X coordinates changed) orientation is 180 - old orient.
2341 // Because it is specific to a footprint, we flip around the X axis, and after rotate 180 deg
2342
2343 MIRROR( finalPos.y, aCentre.y );
2344
2345 SetPosition( finalPos );
2346
2347 // Flip layer
2349
2350 // Calculate the new orientation, and then clear it for pad flipping.
2351 EDA_ANGLE newOrientation = -m_orient;
2352 newOrientation.Normalize180();
2353 m_orient = ANGLE_0;
2354
2355 // Mirror fields to other side of board.
2356 for( PCB_FIELD* field : m_fields )
2357 field->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2358
2359 // Mirror pads to other side of board.
2360 for( PAD* pad : m_pads )
2361 pad->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2362
2363 // Now set the new orientation.
2364 m_orient = newOrientation;
2365
2366 // Mirror zones to other side of board.
2367 for( ZONE* zone : m_zones )
2368 zone->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2369
2370 // Reverse mirror footprint graphics and texts.
2371 for( BOARD_ITEM* item : m_drawings )
2372 item->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
2373
2374 // Now rotate 180 deg if required
2375 if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
2376 Rotate( aCentre, ANGLE_180 );
2377
2380
2381 m_cachedHull.Mirror( m_pos, aFlipDirection );
2382
2384}
2385
2386
2388{
2389 VECTOR2I delta = aPos - m_pos;
2390
2391 m_pos += delta;
2392
2393 for( PCB_FIELD* field : m_fields )
2394 field->EDA_TEXT::Offset( delta );
2395
2396 for( PAD* pad : m_pads )
2397 pad->SetPosition( pad->GetPosition() + delta );
2398
2399 for( ZONE* zone : m_zones )
2400 zone->Move( delta );
2401
2402 for( BOARD_ITEM* item : m_drawings )
2403 item->Move( delta );
2404
2410}
2411
2412
2413void FOOTPRINT::MoveAnchorPosition( const VECTOR2I& aMoveVector )
2414{
2415 /*
2416 * Move the reference point of the footprint
2417 * the footprints elements (pads, outlines, edges .. ) are moved
2418 * but:
2419 * - the footprint position is not modified.
2420 * - the relative (local) coordinates of these items are modified
2421 * - Draw coordinates are updated
2422 */
2423
2424 // Update (move) the relative coordinates relative to the new anchor point.
2425 VECTOR2I moveVector = aMoveVector;
2426 RotatePoint( moveVector, -GetOrientation() );
2427
2428 // Update field local coordinates
2429 for( PCB_FIELD* field : m_fields )
2430 field->Move( moveVector );
2431
2432 // Update the pad local coordinates.
2433 for( PAD* pad : m_pads )
2434 pad->Move( moveVector );
2435
2436 // Update the draw element coordinates.
2437 for( BOARD_ITEM* item : GraphicalItems() )
2438 item->Move( moveVector );
2439
2440 // Update the keepout zones
2441 for( ZONE* zone : Zones() )
2442 zone->Move( moveVector );
2443
2444 // Update the 3D models
2445 for( FP_3DMODEL& model : Models() )
2446 {
2447 model.m_Offset.x += pcbIUScale.IUTomm( moveVector.x );
2448 model.m_Offset.y -= pcbIUScale.IUTomm( moveVector.y );
2449 }
2450
2451 m_cachedBoundingBox.Move( moveVector );
2452 m_cachedTextExcludedBBox.Move( moveVector );
2453 m_cachedHull.Move( moveVector );
2454}
2455
2456
2457void FOOTPRINT::SetOrientation( const EDA_ANGLE& aNewAngle )
2458{
2459 EDA_ANGLE angleChange = aNewAngle - m_orient; // change in rotation
2460
2461 m_orient = aNewAngle;
2463
2464 for( PCB_FIELD* field : m_fields )
2465 field->Rotate( GetPosition(), angleChange );
2466
2467 for( PAD* pad : m_pads )
2468 pad->Rotate( GetPosition(), angleChange );
2469
2470 for( ZONE* zone : m_zones )
2471 zone->Rotate( GetPosition(), angleChange );
2472
2473 for( BOARD_ITEM* item : m_drawings )
2474 item->Rotate( GetPosition(), angleChange );
2475
2478
2479 m_cachedHull.Rotate( angleChange, GetPosition() );
2480}
2481
2482
2484{
2485 FOOTPRINT* dupe = static_cast<FOOTPRINT*>( BOARD_ITEM::Duplicate() );
2486
2487 dupe->RunOnDescendants( [&]( BOARD_ITEM* child )
2488 {
2489 const_cast<KIID&>( child->m_Uuid ) = KIID();
2490 });
2491
2492 return dupe;
2493}
2494
2495
2496BOARD_ITEM* FOOTPRINT::DuplicateItem( const BOARD_ITEM* aItem, bool aAddToFootprint )
2497{
2498 BOARD_ITEM* new_item = nullptr;
2499
2500 switch( aItem->Type() )
2501 {
2502 case PCB_PAD_T:
2503 {
2504 PAD* new_pad = new PAD( *static_cast<const PAD*>( aItem ) );
2505 const_cast<KIID&>( new_pad->m_Uuid ) = KIID();
2506
2507 if( aAddToFootprint )
2508 m_pads.push_back( new_pad );
2509
2510 new_item = new_pad;
2511 break;
2512 }
2513
2514 case PCB_ZONE_T:
2515 {
2516 ZONE* new_zone = new ZONE( *static_cast<const ZONE*>( aItem ) );
2517 const_cast<KIID&>( new_zone->m_Uuid ) = KIID();
2518
2519 if( aAddToFootprint )
2520 m_zones.push_back( new_zone );
2521
2522 new_item = new_zone;
2523 break;
2524 }
2525
2526 case PCB_FIELD_T:
2527 case PCB_TEXT_T:
2528 {
2529 PCB_TEXT* new_text = new PCB_TEXT( *static_cast<const PCB_TEXT*>( aItem ) );
2530 const_cast<KIID&>( new_text->m_Uuid ) = KIID();
2531
2532 if( aItem->Type() == PCB_FIELD_T )
2533 {
2534 switch( static_cast<const PCB_FIELD*>( aItem )->GetId() )
2535 {
2536 case REFERENCE_FIELD: new_text->SetText( wxT( "${REFERENCE}" ) ); break;
2537
2538 case VALUE_FIELD: new_text->SetText( wxT( "${VALUE}" ) ); break;
2539
2540 case DATASHEET_FIELD: new_text->SetText( wxT( "${DATASHEET}" ) ); break;
2541 }
2542 }
2543
2544 if( aAddToFootprint )
2545 Add( new_text );
2546
2547 new_item = new_text;
2548 break;
2549 }
2550
2551 case PCB_SHAPE_T:
2552 {
2553 PCB_SHAPE* new_shape = new PCB_SHAPE( *static_cast<const PCB_SHAPE*>( aItem ) );
2554 const_cast<KIID&>( new_shape->m_Uuid ) = KIID();
2555
2556 if( aAddToFootprint )
2557 Add( new_shape );
2558
2559 new_item = new_shape;
2560 break;
2561 }
2562
2563 case PCB_TEXTBOX_T:
2564 {
2565 PCB_TEXTBOX* new_textbox = new PCB_TEXTBOX( *static_cast<const PCB_TEXTBOX*>( aItem ) );
2566 const_cast<KIID&>( new_textbox->m_Uuid ) = KIID();
2567
2568 if( aAddToFootprint )
2569 Add( new_textbox );
2570
2571 new_item = new_textbox;
2572 break;
2573 }
2574
2575 case PCB_DIM_ALIGNED_T:
2576 case PCB_DIM_LEADER_T:
2577 case PCB_DIM_CENTER_T:
2578 case PCB_DIM_RADIAL_T:
2580 {
2581 PCB_DIMENSION_BASE* dimension = static_cast<PCB_DIMENSION_BASE*>( aItem->Duplicate() );
2582
2583 if( aAddToFootprint )
2584 Add( dimension );
2585
2586 new_item = dimension;
2587 break;
2588 }
2589
2590 case PCB_GROUP_T:
2591 {
2592 PCB_GROUP* group = static_cast<const PCB_GROUP*>( aItem )->DeepDuplicate();
2593
2594 if( aAddToFootprint )
2595 {
2596 group->RunOnDescendants(
2597 [&]( BOARD_ITEM* aCurrItem )
2598 {
2599 Add( aCurrItem );
2600 } );
2601
2602 Add( new_item );
2603 }
2604
2605 new_item = group;
2606 break;
2607 }
2608
2609 case PCB_FOOTPRINT_T:
2610 // Ignore the footprint itself
2611 break;
2612
2613 default:
2614 // Un-handled item for duplication
2615 wxFAIL_MSG( wxT( "Duplication not supported for items of class " ) + aItem->GetClass() );
2616 break;
2617 }
2618
2619 return new_item;
2620}
2621
2622
2623wxString FOOTPRINT::GetNextPadNumber( const wxString& aLastPadNumber ) const
2624{
2625 std::set<wxString> usedNumbers;
2626
2627 // Create a set of used pad numbers
2628 for( PAD* pad : m_pads )
2629 usedNumbers.insert( pad->GetNumber() );
2630
2631 // Pad numbers aren't technically reference designators, but the formatting is close enough
2632 // for these to give us what we need.
2633 wxString prefix = UTIL::GetRefDesPrefix( aLastPadNumber );
2634 int num = GetTrailingInt( aLastPadNumber );
2635
2636 while( usedNumbers.count( wxString::Format( wxT( "%s%d" ), prefix, num ) ) )
2637 num++;
2638
2639 return wxString::Format( wxT( "%s%d" ), prefix, num );
2640}
2641
2642
2644{
2645 // Auto-position reference and value
2646 BOX2I bbox = GetBoundingBox( false );
2647 bbox.Inflate( pcbIUScale.mmToIU( 0.2 ) ); // Gap between graphics and text
2648
2649 if( Reference().GetPosition() == VECTOR2I( 0, 0 ) )
2650 {
2654
2655 Reference().SetX( bbox.GetCenter().x );
2656 Reference().SetY( bbox.GetTop() - Reference().GetTextSize().y / 2 );
2657 }
2658
2659 if( Value().GetPosition() == VECTOR2I( 0, 0 ) )
2660 {
2664
2665 Value().SetX( bbox.GetCenter().x );
2666 Value().SetY( bbox.GetBottom() + Value().GetTextSize().y / 2 );
2667 }
2668}
2669
2670
2672{
2673 const wxString& refdes = GetReference();
2674
2675 SetReference( wxString::Format( wxT( "%s%i" ),
2676 UTIL::GetRefDesPrefix( refdes ),
2677 GetTrailingInt( refdes ) + aDelta ) );
2678}
2679
2680
2681// Calculate the area of a PolySet, polygons with hole are allowed.
2682static double polygonArea( SHAPE_POLY_SET& aPolySet )
2683{
2684 // Ensure all outlines are closed, before calculating the SHAPE_POLY_SET area
2685 for( int ii = 0; ii < aPolySet.OutlineCount(); ii++ )
2686 {
2687 SHAPE_LINE_CHAIN& outline = aPolySet.Outline( ii );
2688 outline.SetClosed( true );
2689
2690 for( int jj = 0; jj < aPolySet.HoleCount( ii ); jj++ )
2691 aPolySet.Hole( ii, jj ).SetClosed( true );
2692 }
2693
2694 return aPolySet.Area();
2695}
2696
2697
2698double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLECTOR& aCollector )
2699{
2700 int textMargin = aCollector.GetGuide()->Accuracy();
2701 SHAPE_POLY_SET poly;
2702
2703 if( aItem->Type() == PCB_MARKER_T )
2704 {
2705 const PCB_MARKER* marker = static_cast<const PCB_MARKER*>( aItem );
2706 SHAPE_LINE_CHAIN markerShape;
2707
2708 marker->ShapeToPolygon( markerShape );
2709 return markerShape.Area();
2710 }
2711 else if( aItem->Type() == PCB_GROUP_T || aItem->Type() == PCB_GENERATOR_T )
2712 {
2713 double combinedArea = 0.0;
2714
2715 for( BOARD_ITEM* member : static_cast<const PCB_GROUP*>( aItem )->GetItems() )
2716 combinedArea += GetCoverageArea( member, aCollector );
2717
2718 return combinedArea;
2719 }
2720 if( aItem->Type() == PCB_FOOTPRINT_T )
2721 {
2722 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( aItem );
2723
2724 poly = footprint->GetBoundingHull();
2725 }
2726 else if( aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T )
2727 {
2728 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aItem );
2729
2730 text->TransformTextToPolySet( poly, textMargin, ARC_LOW_DEF, ERROR_INSIDE );
2731 }
2732 else if( aItem->Type() == PCB_TEXTBOX_T )
2733 {
2734 const PCB_TEXTBOX* tb = static_cast<const PCB_TEXTBOX*>( aItem );
2735
2736 tb->TransformTextToPolySet( poly, textMargin, ARC_LOW_DEF, ERROR_INSIDE );
2737 }
2738 else if( aItem->Type() == PCB_SHAPE_T )
2739 {
2740 // Approximate "linear" shapes with just their width squared, as we don't want to consider
2741 // a linear shape as being much bigger than another for purposes of selection filtering
2742 // just because it happens to be really long.
2743
2744 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
2745
2746 switch( shape->GetShape() )
2747 {
2748 case SHAPE_T::SEGMENT:
2749 case SHAPE_T::ARC:
2750 case SHAPE_T::BEZIER:
2751 return shape->GetWidth() * shape->GetWidth();
2752
2753 case SHAPE_T::RECTANGLE:
2754 case SHAPE_T::CIRCLE:
2755 case SHAPE_T::POLY:
2756 {
2757 if( !shape->IsFilled() )
2758 return shape->GetWidth() * shape->GetWidth();
2759
2761 }
2762
2763 default:
2765 }
2766 }
2767 else if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
2768 {
2769 double width = static_cast<const PCB_TRACK*>( aItem )->GetWidth();
2770 return width * width;
2771 }
2772 else if( aItem->Type() == PCB_PAD_T )
2773 {
2774 static_cast<const PAD*>( aItem )->Padstack().ForEachUniqueLayer(
2775 [&]( PCB_LAYER_ID aLayer )
2776 {
2777 SHAPE_POLY_SET padPoly;
2778 aItem->TransformShapeToPolygon( padPoly, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE );
2779 poly.BooleanAdd( padPoly, SHAPE_POLY_SET::PM_FAST );
2780 } );
2781 }
2782 else
2783 {
2785 }
2786
2787 return polygonArea( poly );
2788}
2789
2790
2791double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
2792{
2793 int textMargin = aCollector.GetGuide()->Accuracy();
2794
2795 SHAPE_POLY_SET footprintRegion( GetBoundingHull() );
2796 SHAPE_POLY_SET coveredRegion;
2797
2799
2800 TransformFPShapesToPolySet( coveredRegion, UNDEFINED_LAYER, textMargin, ARC_LOW_DEF,
2802 true, /* include text */
2803 false, /* include shapes */
2804 false /* include private items */ );
2805
2806 for( int i = 0; i < aCollector.GetCount(); ++i )
2807 {
2808 const BOARD_ITEM* item = aCollector[i];
2809
2810 switch( item->Type() )
2811 {
2812 case PCB_FIELD_T:
2813 case PCB_TEXT_T:
2814 case PCB_TEXTBOX_T:
2815 case PCB_SHAPE_T:
2816 case PCB_TRACE_T:
2817 case PCB_ARC_T:
2818 case PCB_VIA_T:
2819 if( item->GetParent() != this )
2820 {
2821 item->TransformShapeToPolygon( coveredRegion, UNDEFINED_LAYER, 0, ARC_LOW_DEF,
2822 ERROR_OUTSIDE );
2823 }
2824 break;
2825
2826 case PCB_FOOTPRINT_T:
2827 if( item != this )
2828 {
2829 const FOOTPRINT* footprint = static_cast<const FOOTPRINT*>( item );
2830 coveredRegion.AddOutline( footprint->GetBoundingHull().Outline( 0 ) );
2831 }
2832 break;
2833
2834 default:
2835 break;
2836 }
2837 }
2838
2839 coveredRegion.BooleanIntersection( footprintRegion, SHAPE_POLY_SET::PM_FAST );
2840
2841 double footprintRegionArea = polygonArea( footprintRegion );
2842 double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion );
2843 double coveredArea = footprintRegionArea - uncoveredRegionArea;
2844 double ratio = ( coveredArea / footprintRegionArea );
2845
2846 // Test for negative ratio (should not occur).
2847 // better to be conservative (this will result in the disambiguate dialog)
2848 if( ratio < 0.0 )
2849 return 1.0;
2850
2851 return std::min( ratio, 1.0 );
2852}
2853
2854
2855std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
2856{
2857 std::shared_ptr<SHAPE_COMPOUND> shape = std::make_shared<SHAPE_COMPOUND>();
2858
2859 // There are several possible interpretations here:
2860 // 1) the bounding box (without or without invisible items)
2861 // 2) just the pads and "edges" (ie: non-text graphic items)
2862 // 3) the courtyard
2863
2864 // We'll go with (2) for now, unless the caller is clearly looking for (3)
2865
2866 if( aLayer == F_CrtYd || aLayer == B_CrtYd )
2867 {
2868 const SHAPE_POLY_SET& courtyard = GetCourtyard( aLayer );
2869
2870 if( courtyard.OutlineCount() == 0 ) // malformed/empty polygon
2871 return shape;
2872
2873 shape->AddShape( new SHAPE_SIMPLE( courtyard.COutline( 0 ) ) );
2874 }
2875 else
2876 {
2877 for( PAD* pad : Pads() )
2878 shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
2879
2880 for( BOARD_ITEM* item : GraphicalItems() )
2881 {
2882 if( item->Type() == PCB_SHAPE_T )
2883 shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
2884 }
2885 }
2886
2887 return shape;
2888}
2889
2890
2892{
2893 std::lock_guard<std::mutex> lock( m_courtyard_cache_mutex );
2894
2897 {
2898 const_cast<FOOTPRINT*>( this )->BuildCourtyardCaches();
2899 }
2900
2901 return GetCachedCourtyard( aLayer );
2902}
2903
2904
2906{
2907 if( IsBackLayer( aLayer ) )
2909 else
2911}
2912
2913
2915{
2919
2920 // Build the courtyard area from graphic items on the courtyard.
2921 // Only PCB_SHAPE_T have meaning, graphic texts are ignored.
2922 // Collect items:
2923 std::vector<PCB_SHAPE*> list_front;
2924 std::vector<PCB_SHAPE*> list_back;
2925 std::map<int, int> front_width_histogram;
2926 std::map<int, int> back_width_histogram;
2927
2928 for( BOARD_ITEM* item : GraphicalItems() )
2929 {
2930 if( item->GetLayer() == B_CrtYd && item->Type() == PCB_SHAPE_T )
2931 {
2932 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2933 list_back.push_back( shape );
2934 back_width_histogram[ shape->GetStroke().GetWidth() ]++;
2935 }
2936
2937 if( item->GetLayer() == F_CrtYd && item->Type() == PCB_SHAPE_T )
2938 {
2939 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2940 list_front.push_back( shape );
2941 front_width_histogram[ shape->GetStroke().GetWidth() ]++;
2942 }
2943 }
2944
2945 if( !list_front.size() && !list_back.size() )
2946 return;
2947
2948 int maxError = pcbIUScale.mmToIU( 0.005 ); // max error for polygonization
2949 int chainingEpsilon = pcbIUScale.mmToIU( 0.02 ); // max dist from one endPt to next startPt
2950
2951 if( ConvertOutlineToPolygon( list_front, m_courtyard_cache_front, maxError, chainingEpsilon,
2952 true, aErrorHandler ) )
2953 {
2954 int width = 0;
2955
2956 // Touching courtyards, or courtyards -at- the clearance distance are legal.
2957 // Use maxError here because that is the allowed deviation when transforming arcs/circles to
2958 // polygons.
2959 m_courtyard_cache_front.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
2960
2962 auto max = std::max_element( front_width_histogram.begin(), front_width_histogram.end(),
2963 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
2964 {
2965 return a.second < b.second;
2966 } );
2967
2968 if( max != front_width_histogram.end() )
2969 width = max->first;
2970
2971 if( width == 0 )
2973
2976 }
2977 else
2978 {
2980 }
2981
2982 if( ConvertOutlineToPolygon( list_back, m_courtyard_cache_back, maxError, chainingEpsilon, true,
2983 aErrorHandler ) )
2984 {
2985 int width = 0;
2986
2987 // Touching courtyards, or courtyards -at- the clearance distance are legal.
2988 m_courtyard_cache_back.Inflate( -maxError, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, maxError );
2989
2991 auto max = std::max_element( back_width_histogram.begin(), back_width_histogram.end(),
2992 []( const std::pair<int, int>& a, const std::pair<int, int>& b )
2993 {
2994 return a.second < b.second;
2995 } );
2996
2997 if( max != back_width_histogram.end() )
2998 width = max->first;
2999
3000 if( width == 0 )
3002
3005 }
3006 else
3007 {
3009 }
3010
3013}
3014
3015
3017{
3018 m_netTieCache.clear();
3019 std::map<wxString, int> map = MapPadNumbersToNetTieGroups();
3020 std::map<PCB_LAYER_ID, std::vector<PCB_SHAPE*>> layer_shapes;
3021
3022 std::for_each( m_drawings.begin(), m_drawings.end(),
3023 [&]( BOARD_ITEM* item )
3024 {
3025 if( item->Type() != PCB_SHAPE_T )
3026 return;
3027
3028 for( PCB_LAYER_ID layer : item->GetLayerSet() )
3029 layer_shapes[layer].push_back( static_cast<PCB_SHAPE*>( item ) );
3030 } );
3031
3032 for( size_t ii = 0; ii < m_pads.size(); ++ii )
3033 {
3034 PAD* pad = m_pads[ ii ];
3035 bool has_nettie = false;
3036
3037 auto it = map.find( pad->GetNumber() );
3038
3039 if( it == map.end() || it->second < 0 )
3040 continue;
3041
3042 for( size_t jj = ii + 1; jj < m_pads.size(); ++jj )
3043 {
3044 PAD* other = m_pads[ jj ];
3045
3046 auto it2 = map.find( other->GetNumber() );
3047
3048 if( it2 == map.end() || it2->second < 0 )
3049 continue;
3050
3051 if( it2->second == it->second )
3052 {
3053 m_netTieCache[pad].insert( pad->GetNetCode() );
3054 m_netTieCache[pad].insert( other->GetNetCode() );
3055 m_netTieCache[other].insert( other->GetNetCode() );
3056 m_netTieCache[other].insert( pad->GetNetCode() );
3057 has_nettie = true;
3058 }
3059 }
3060
3061 if( !has_nettie )
3062 continue;
3063
3064 for( auto& [ layer, shapes ] : layer_shapes )
3065 {
3066 auto pad_shape = pad->GetEffectiveShape( layer );
3067
3068 for( auto other_shape : shapes )
3069 {
3070 auto shape = other_shape->GetEffectiveShape( layer );
3071
3072 if( pad_shape->Collide( shape.get() ) )
3073 {
3074 std::set<int>& nettie = m_netTieCache[pad];
3075 m_netTieCache[other_shape].insert( nettie.begin(), nettie.end() );
3076 }
3077 }
3078 }
3079 }
3080}
3081
3082
3083std::map<wxString, int> FOOTPRINT::MapPadNumbersToNetTieGroups() const
3084{
3085 std::map<wxString, int> padNumberToGroupIdxMap;
3086
3087 for( const PAD* pad : m_pads )
3088 padNumberToGroupIdxMap[ pad->GetNumber() ] = -1;
3089
3090 auto processPad =
3091 [&]( wxString aPad, int aGroup )
3092 {
3093 aPad.Trim( true ).Trim( false );
3094
3095 if( !aPad.IsEmpty() )
3096 padNumberToGroupIdxMap[ aPad ] = aGroup;
3097 };
3098
3099 for( int ii = 0; ii < (int) m_netTiePadGroups.size(); ++ii )
3100 {
3101 wxString group( m_netTiePadGroups[ ii ] );
3102 bool esc = false;
3103 wxString pad;
3104
3105 for( wxUniCharRef ch : group )
3106 {
3107 if( esc )
3108 {
3109 esc = false;
3110 pad.Append( ch );
3111 continue;
3112 }
3113
3114 switch( static_cast<unsigned char>( ch ) )
3115 {
3116 case '\\':
3117 esc = true;
3118 break;
3119
3120 case ',':
3121 processPad( pad, ii );
3122 pad.Clear();
3123 break;
3124
3125 default:
3126 pad.Append( ch );
3127 break;
3128 }
3129 }
3130
3131 processPad( pad, ii );
3132 }
3133
3134 return padNumberToGroupIdxMap;
3135}
3136
3137
3138std::vector<PAD*> FOOTPRINT::GetNetTiePads( PAD* aPad ) const
3139{
3140 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
3141 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
3142
3143 std::map<wxString, int> padToNetTieGroupMap = MapPadNumbersToNetTieGroups();
3144 int groupIdx = padToNetTieGroupMap[ aPad->GetNumber() ];
3145 std::vector<PAD*> otherPads;
3146
3147 if( groupIdx >= 0 )
3148 {
3149 for( PAD* pad : m_pads )
3150 {
3151 if( padToNetTieGroupMap[ pad->GetNumber() ] == groupIdx )
3152 otherPads.push_back( pad );
3153 }
3154 }
3155
3156 return otherPads;
3157}
3158
3159
3160void FOOTPRINT::CheckFootprintAttributes( const std::function<void( const wxString& )>& aErrorHandler )
3161{
3162 int likelyAttr = ( GetLikelyAttribute() & ( FP_SMD | FP_THROUGH_HOLE ) );
3163 int setAttr = ( GetAttributes() & ( FP_SMD | FP_THROUGH_HOLE ) );
3164
3165 if( setAttr && likelyAttr && setAttr != likelyAttr )
3166 {
3167 wxString msg;
3168
3169 switch( likelyAttr )
3170 {
3171 case FP_THROUGH_HOLE:
3172 msg.Printf( _( "(expected 'Through hole'; actual '%s')" ), GetTypeName() );
3173 break;
3174 case FP_SMD:
3175 msg.Printf( _( "(expected 'SMD'; actual '%s')" ), GetTypeName() );
3176 break;
3177 }
3178
3179 if( aErrorHandler )
3180 (aErrorHandler)( msg );
3181 }
3182}
3183
3184
3186 const std::function<void( const PAD*, int,
3187 const wxString& )>& aErrorHandler )
3188{
3189 if( aErrorHandler == nullptr )
3190 return;
3191
3192 for( PAD* pad: Pads() )
3193 {
3194 pad->CheckPad( aUnitsProvider,
3195 [&]( int errorCode, const wxString& msg )
3196 {
3197 aErrorHandler( pad, errorCode, msg );
3198 } );
3199 }
3200}
3201
3202
3203void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const PAD*,
3204 int aErrorCode,
3205 const VECTOR2I& )>& aErrorHandler )
3206{
3207 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
3208
3209 for( PAD* pad : Pads() )
3210 {
3211 std::vector<PAD*> netTiePads = GetNetTiePads( pad );
3212
3213 for( PAD* other : Pads() )
3214 {
3215 if( other == pad )
3216 continue;
3217
3218 // store canonical order so we don't collide in both directions (a:b and b:a)
3219 PAD* a = pad;
3220 PAD* b = other;
3221
3222 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
3223 std::swap( a, b );
3224
3225 if( checkedPairs.find( { a, b } ) == checkedPairs.end() )
3226 {
3227 checkedPairs[ { a, b } ] = 1;
3228
3229 if( pad->HasDrilledHole() && other->HasDrilledHole() )
3230 {
3231 VECTOR2I pos = pad->GetPosition();
3232
3233 if( pad->GetPosition() == other->GetPosition() )
3234 {
3235 aErrorHandler( pad, other, DRCE_DRILLED_HOLES_COLOCATED, pos );
3236 }
3237 else
3238 {
3239 std::shared_ptr<SHAPE_SEGMENT> holeA = pad->GetEffectiveHoleShape();
3240 std::shared_ptr<SHAPE_SEGMENT> holeB = other->GetEffectiveHoleShape();
3241
3242 if( holeA->Collide( holeB->GetSeg(), 0 ) )
3243 aErrorHandler( pad, other, DRCE_DRILLED_HOLES_TOO_CLOSE, pos );
3244 }
3245 }
3246
3247 if( pad->SameLogicalPadAs( other ) || alg::contains( netTiePads, other ) )
3248 continue;
3249
3250 if( !( ( pad->GetLayerSet() & other->GetLayerSet() ) & LSET::AllCuMask() ).any() )
3251 continue;
3252
3253 if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
3254 {
3255 VECTOR2I pos;
3256
3257 for( PCB_LAYER_ID l : pad->Padstack().RelevantShapeLayers( other->Padstack() ) )
3258 {
3259 SHAPE* padShape = pad->GetEffectiveShape( l ).get();
3260 SHAPE* otherShape = other->GetEffectiveShape( l ).get();
3261
3262 if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
3263 aErrorHandler( pad, other, DRCE_SHORTING_ITEMS, pos );
3264 }
3265 }
3266 }
3267 }
3268 }
3269}
3270
3271
3272void FOOTPRINT::CheckNetTies( const std::function<void( const BOARD_ITEM* aItem,
3273 const BOARD_ITEM* bItem,
3274 const BOARD_ITEM* cItem,
3275 const VECTOR2I& )>& aErrorHandler )
3276{
3277 // First build a map from pad numbers to allowed-shorting-group indexes. This ends up being
3278 // something like O(3n), but it still beats O(n^2) for large numbers of pads.
3279
3280 std::map<wxString, int> padNumberToGroupIdxMap = MapPadNumbersToNetTieGroups();
3281
3282 // Now collect all the footprint items which are on copper layers
3283
3284 std::vector<BOARD_ITEM*> copperItems;
3285
3286 for( BOARD_ITEM* item : m_drawings )
3287 {
3288 if( item->IsOnCopperLayer() )
3289 copperItems.push_back( item );
3290
3291 item->RunOnDescendants(
3292 [&]( BOARD_ITEM* descendent )
3293 {
3294 if( descendent->IsOnCopperLayer() )
3295 copperItems.push_back( descendent );
3296 } );
3297 }
3298
3299 for( ZONE* zone : m_zones )
3300 {
3301 if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
3302 copperItems.push_back( zone );
3303 }
3304
3305 for( PCB_FIELD* field : m_fields )
3306 {
3307 if( field->IsOnCopperLayer() )
3308 copperItems.push_back( field );
3309 }
3310
3311 for( PCB_LAYER_ID layer : { F_Cu, In1_Cu, B_Cu } )
3312 {
3313 // Next, build a polygon-set for the copper on this layer. We don't really care about
3314 // nets here, we just want to end up with a set of outlines describing the distinct
3315 // copper polygons of the footprint.
3316
3317 SHAPE_POLY_SET copperOutlines;
3318 std::map<int, std::vector<const PAD*>> outlineIdxToPadsMap;
3319
3320 for( BOARD_ITEM* item : copperItems )
3321 {
3322 if( item->IsOnLayer( layer ) )
3323 {
3324 item->TransformShapeToPolygon( copperOutlines, layer, 0, ARC_HIGH_DEF,
3325 ERROR_OUTSIDE );
3326 }
3327 }
3328
3329 copperOutlines.Simplify( SHAPE_POLY_SET::PM_FAST );
3330
3331 // Index each pad to the outline in the set that it is part of.
3332
3333 for( const PAD* pad : m_pads )
3334 {
3335 for( int ii = 0; ii < copperOutlines.OutlineCount(); ++ii )
3336 {
3337 if( pad->GetEffectiveShape( layer )->Collide( &copperOutlines.Outline( ii ), 0 ) )
3338 outlineIdxToPadsMap[ ii ].emplace_back( pad );
3339 }
3340 }
3341
3342 // Finally, ensure that each outline which contains multiple pads has all its pads
3343 // listed in an allowed-shorting group.
3344
3345 for( const auto& [ outlineIdx, pads ] : outlineIdxToPadsMap )
3346 {
3347 if( pads.size() > 1 )
3348 {
3349 const PAD* firstPad = pads[0];
3350 int firstGroupIdx = padNumberToGroupIdxMap[ firstPad->GetNumber() ];
3351
3352 for( size_t ii = 1; ii < pads.size(); ++ii )
3353 {
3354 const PAD* thisPad = pads[ii];
3355 int thisGroupIdx = padNumberToGroupIdxMap[ thisPad->GetNumber() ];
3356
3357 if( thisGroupIdx < 0 || thisGroupIdx != firstGroupIdx )
3358 {
3359 BOARD_ITEM* shortingItem = nullptr;
3360 VECTOR2I pos = ( firstPad->GetPosition() + thisPad->GetPosition() ) / 2;
3361
3362 pos = copperOutlines.Outline( outlineIdx ).NearestPoint( pos );
3363
3364 for( BOARD_ITEM* item : copperItems )
3365 {
3366 if( item->HitTest( pos, 1 ) )
3367 {
3368 shortingItem = item;
3369 break;
3370 }
3371 }
3372
3373 if( shortingItem )
3374 aErrorHandler( shortingItem, firstPad, thisPad, pos );
3375 else
3376 aErrorHandler( firstPad, thisPad, nullptr, pos );
3377 }
3378 }
3379 }
3380 }
3381 }
3382}
3383
3384
3385void FOOTPRINT::CheckNetTiePadGroups( const std::function<void( const wxString& )>& aErrorHandler )
3386{
3387 std::set<wxString> padNumbers;
3388 wxString msg;
3389
3390 for( const auto& [ padNumber, _ ] : MapPadNumbersToNetTieGroups() )
3391 {
3392 const PAD* pad = FindPadByNumber( padNumber );
3393
3394 if( !pad )
3395 {
3396 msg.Printf( _( "(net-tie pad group contains unknown pad number %s)" ), padNumber );
3397 aErrorHandler( msg );
3398 }
3399 else if( !padNumbers.insert( pad->GetNumber() ).second )
3400 {
3401 msg.Printf( _( "(pad %s appears in more than one net-tie pad group)" ), padNumber );
3402 aErrorHandler( msg );
3403 }
3404 }
3405}
3406
3407
3409{
3410 wxASSERT( aImage->Type() == PCB_FOOTPRINT_T );
3411
3412 FOOTPRINT* image = static_cast<FOOTPRINT*>( aImage );
3413
3414 std::swap( *this, *image );
3415
3417 [&]( BOARD_ITEM* child )
3418 {
3419 child->SetParent( this );
3420 } );
3421
3422 image->RunOnChildren(
3423 [&]( BOARD_ITEM* child )
3424 {
3425 child->SetParent( image );
3426 } );
3427}
3428
3429
3431{
3432 for( PAD* pad : Pads() )
3433 {
3434 if( pad->GetAttribute() != PAD_ATTRIB::SMD )
3435 return true;
3436 }
3437
3438 return false;
3439}
3440
3441
3442bool FOOTPRINT::operator==( const BOARD_ITEM& aOther ) const
3443{
3444 if( aOther.Type() != PCB_FOOTPRINT_T )
3445 return false;
3446
3447 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
3448
3449 return *this == other;
3450}
3451
3452
3453bool FOOTPRINT::operator==( const FOOTPRINT& aOther ) const
3454{
3455 if( m_pads.size() != aOther.m_pads.size() )
3456 return false;
3457
3458 for( size_t ii = 0; ii < m_pads.size(); ++ii )
3459 {
3460 if( !( *m_pads[ii] == *aOther.m_pads[ii] ) )
3461 return false;
3462 }
3463
3464 if( m_drawings.size() != aOther.m_drawings.size() )
3465 return false;
3466
3467 for( size_t ii = 0; ii < m_drawings.size(); ++ii )
3468 {
3469 if( !( *m_drawings[ii] == *aOther.m_drawings[ii] ) )
3470 return false;
3471 }
3472
3473 if( m_zones.size() != aOther.m_zones.size() )
3474 return false;
3475
3476 for( size_t ii = 0; ii < m_zones.size(); ++ii )
3477 {
3478 if( !( *m_zones[ii] == *aOther.m_zones[ii] ) )
3479 return false;
3480 }
3481
3482 if( m_fields.size() != aOther.m_fields.size() )
3483 return false;
3484
3485 for( size_t ii = 0; ii < m_fields.size(); ++ii )
3486 {
3487 if( !( *m_fields[ii] == *aOther.m_fields[ii] ) )
3488 return false;
3489 }
3490
3491 return true;
3492}
3493
3494
3495double FOOTPRINT::Similarity( const BOARD_ITEM& aOther ) const
3496{
3497 if( aOther.Type() != PCB_FOOTPRINT_T )
3498 return 0.0;
3499
3500 const FOOTPRINT& other = static_cast<const FOOTPRINT&>( aOther );
3501
3502 double similarity = 1.0;
3503
3504 for( const PAD* pad : m_pads)
3505 {
3506 const PAD* otherPad = other.FindPadByNumber( pad->GetNumber() );
3507
3508 if( !otherPad )
3509 continue;
3510
3511 similarity *= pad->Similarity( *otherPad );
3512 }
3513
3514 return similarity;
3515}
3516
3517
3518bool FOOTPRINT::cmp_drawings::operator()( const BOARD_ITEM* itemA, const BOARD_ITEM* itemB ) const
3519{
3520 if( itemA->Type() != itemB->Type() )
3521 return itemA->Type() < itemB->Type();
3522
3523 if( itemA->GetLayer() != itemB->GetLayer() )
3524 return itemA->GetLayer() < itemB->GetLayer();
3525
3526 if( itemA->Type() == PCB_SHAPE_T )
3527 {
3528 const PCB_SHAPE* dwgA = static_cast<const PCB_SHAPE*>( itemA );
3529 const PCB_SHAPE* dwgB = static_cast<const PCB_SHAPE*>( itemB );
3530
3531 if( dwgA->GetShape() != dwgB->GetShape() )
3532 return dwgA->GetShape() < dwgB->GetShape();
3533
3534 // GetStart() and GetEnd() have no meaning with polygons.
3535 // We cannot use them for sorting polygons
3536 if( dwgA->GetShape() != SHAPE_T::POLY )
3537 {
3538 if( dwgA->GetStart().x != dwgB->GetStart().x )
3539 return dwgA->GetStart().x < dwgB->GetStart().x;
3540 if( dwgA->GetStart().y != dwgB->GetStart().y )
3541 return dwgA->GetStart().y < dwgB->GetStart().y;
3542
3543 if( dwgA->GetEnd().x != dwgB->GetEnd().x )
3544 return dwgA->GetEnd().x < dwgB->GetEnd().x;
3545 if( dwgA->GetEnd().y != dwgB->GetEnd().y )
3546 return dwgA->GetEnd().y < dwgB->GetEnd().y;
3547 }
3548
3549 if( dwgA->GetShape() == SHAPE_T::ARC )
3550 {
3551 if( dwgA->GetCenter().x != dwgB->GetCenter().x )
3552 return dwgA->GetCenter().x < dwgB->GetCenter().x;
3553 if( dwgA->GetCenter().y != dwgB->GetCenter().y )
3554 return dwgA->GetCenter().y < dwgB->GetCenter().y;
3555 }
3556 else if( dwgA->GetShape() == SHAPE_T::BEZIER )
3557 {
3558 if( dwgA->GetBezierC1().x != dwgB->GetBezierC1().x )
3559 return dwgA->GetBezierC1().x < dwgB->GetBezierC1().x;
3560 if( dwgA->GetBezierC1().y != dwgB->GetBezierC1().y )
3561 return dwgA->GetBezierC1().y < dwgB->GetBezierC1().y;
3562
3563 if( dwgA->GetBezierC2().x != dwgB->GetBezierC2().x )
3564 return dwgA->GetBezierC2().x < dwgB->GetBezierC2().x;
3565 if( dwgA->GetBezierC2().y != dwgB->GetBezierC2().y )
3566 return dwgA->GetBezierC2().y < dwgB->GetBezierC2().y;
3567 }
3568 else if( dwgA->GetShape() == SHAPE_T::POLY )
3569 {
3570 if( dwgA->GetPolyShape().TotalVertices() != dwgB->GetPolyShape().TotalVertices() )
3571 return dwgA->GetPolyShape().TotalVertices() < dwgB->GetPolyShape().TotalVertices();
3572
3573 for( int ii = 0; ii < dwgA->GetPolyShape().TotalVertices(); ++ii )
3574 {
3575 if( dwgA->GetPolyShape().CVertex( ii ).x != dwgB->GetPolyShape().CVertex( ii ).x )
3576 return dwgA->GetPolyShape().CVertex( ii ).x
3577 < dwgB->GetPolyShape().CVertex( ii ).x;
3578 if( dwgA->GetPolyShape().CVertex( ii ).y != dwgB->GetPolyShape().CVertex( ii ).y )
3579 return dwgA->GetPolyShape().CVertex( ii ).y
3580 < dwgB->GetPolyShape().CVertex( ii ).y;
3581 }
3582 }
3583
3584 if( dwgA->GetWidth() != dwgB->GetWidth() )
3585 return dwgA->GetWidth() < dwgB->GetWidth();
3586 }
3587
3588 if( itemA->m_Uuid != itemB->m_Uuid )
3589 return itemA->m_Uuid < itemB->m_Uuid;
3590
3591 return itemA < itemB;
3592}
3593
3594
3595bool FOOTPRINT::cmp_pads::operator()( const PAD* aFirst, const PAD* aSecond ) const
3596{
3597 if( aFirst->GetNumber() != aSecond->GetNumber() )
3598 return StrNumCmp( aFirst->GetNumber(), aSecond->GetNumber() ) < 0;
3599
3600 if( aFirst->GetFPRelativePosition().x != aSecond->GetFPRelativePosition().x )
3601 return aFirst->GetFPRelativePosition().x < aSecond->GetFPRelativePosition().x;
3602 if( aFirst->GetFPRelativePosition().y != aSecond->GetFPRelativePosition().y )
3603 return aFirst->GetFPRelativePosition().y < aSecond->GetFPRelativePosition().y;
3604
3605 std::optional<bool> padCopperMatches;
3606
3607 // Pick the "most complex" padstack to iterate
3608 const PAD* checkPad = aFirst;
3609
3610 if( aSecond->Padstack().Mode() == PADSTACK::MODE::CUSTOM
3611 || ( aSecond->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK &&
3612 aFirst->Padstack().Mode() == PADSTACK::MODE::NORMAL ) )
3613 {
3614 checkPad = aSecond;
3615 }
3616
3617 checkPad->Padstack().ForEachUniqueLayer(
3618 [&]( PCB_LAYER_ID aLayer )
3619 {
3620 if( aFirst->GetSize( aLayer ).x != aSecond->GetSize( aLayer ).x )
3621 padCopperMatches = aFirst->GetSize( aLayer ).x < aSecond->GetSize( aLayer ).x;
3622 else if( aFirst->GetSize( aLayer ).y != aSecond->GetSize( aLayer ).y )
3623 padCopperMatches = aFirst->GetSize( aLayer ).y < aSecond->GetSize( aLayer ).y;
3624 else if( aFirst->GetShape( aLayer ) != aSecond->GetShape( aLayer ) )
3625 padCopperMatches = aFirst->GetShape( aLayer ) < aSecond->GetShape( aLayer );
3626 } );
3627
3628 if( padCopperMatches.has_value() )
3629 return *padCopperMatches;
3630
3631 if( aFirst->GetLayerSet().Seq() != aSecond->GetLayerSet().Seq() )
3632 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
3633
3634 if( aFirst->m_Uuid != aSecond->m_Uuid )
3635 return aFirst->m_Uuid < aSecond->m_Uuid;
3636
3637 return aFirst < aSecond;
3638}
3639
3640
3641#if 0
3642bool FOOTPRINT::cmp_padstack::operator()( const PAD* aFirst, const PAD* aSecond ) const
3643{
3644 if( aFirst->GetSize().x != aSecond->GetSize().x )
3645 return aFirst->GetSize().x < aSecond->GetSize().x;
3646 if( aFirst->GetSize().y != aSecond->GetSize().y )
3647 return aFirst->GetSize().y < aSecond->GetSize().y;
3648
3649 if( aFirst->GetShape() != aSecond->GetShape() )
3650 return aFirst->GetShape() < aSecond->GetShape();
3651
3652 if( aFirst->GetLayerSet().Seq() != aSecond->GetLayerSet().Seq() )
3653 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
3654
3655 if( aFirst->GetDrillSizeX() != aSecond->GetDrillSizeX() )
3656 return aFirst->GetDrillSizeX() < aSecond->GetDrillSizeX();
3657
3658 if( aFirst->GetDrillSizeY() != aSecond->GetDrillSizeY() )
3659 return aFirst->GetDrillSizeY() < aSecond->GetDrillSizeY();
3660
3661 if( aFirst->GetDrillShape() != aSecond->GetDrillShape() )
3662 return aFirst->GetDrillShape() < aSecond->GetDrillShape();
3663
3664 if( aFirst->GetAttribute() != aSecond->GetAttribute() )
3665 return aFirst->GetAttribute() < aSecond->GetAttribute();
3666
3667 if( aFirst->GetOrientation() != aSecond->GetOrientation() )
3668 return aFirst->GetOrientation() < aSecond->GetOrientation();
3669
3670 if( aFirst->GetSolderMaskExpansion() != aSecond->GetSolderMaskExpansion() )
3671 return aFirst->GetSolderMaskExpansion() < aSecond->GetSolderMaskExpansion();
3672
3673 if( aFirst->GetSolderPasteMargin() != aSecond->GetSolderPasteMargin() )
3674 return aFirst->GetSolderPasteMargin() < aSecond->GetSolderPasteMargin();
3675
3676 if( aFirst->GetLocalSolderMaskMargin() != aSecond->GetLocalSolderMaskMargin() )
3677 return aFirst->GetLocalSolderMaskMargin() < aSecond->GetLocalSolderMaskMargin();
3678
3679 const std::shared_ptr<SHAPE_POLY_SET>& firstShape = aFirst->GetEffectivePolygon( ERROR_INSIDE );
3680 const std::shared_ptr<SHAPE_POLY_SET>& secondShape = aSecond->GetEffectivePolygon( ERROR_INSIDE );
3681
3682 if( firstShape->VertexCount() != secondShape->VertexCount() )
3683 return firstShape->VertexCount() < secondShape->VertexCount();
3684
3685 for( int ii = 0; ii < firstShape->VertexCount(); ++ii )
3686 {
3687 if( firstShape->CVertex( ii ).x != secondShape->CVertex( ii ).x )
3688 return firstShape->CVertex( ii ).x < secondShape->CVertex( ii ).x;
3689 if( firstShape->CVertex( ii ).y != secondShape->CVertex( ii ).y )
3690 return firstShape->CVertex( ii ).y < secondShape->CVertex( ii ).y;
3691 }
3692
3693 return false;
3694}
3695#endif
3696
3697
3698bool FOOTPRINT::cmp_zones::operator()( const ZONE* aFirst, const ZONE* aSecond ) const
3699{
3700 if( aFirst->GetAssignedPriority() != aSecond->GetAssignedPriority() )
3701 return aFirst->GetAssignedPriority() < aSecond->GetAssignedPriority();
3702
3703 if( aFirst->GetLayerSet().Seq() != aSecond->GetLayerSet().Seq() )
3704 return aFirst->GetLayerSet().Seq() < aSecond->GetLayerSet().Seq();
3705
3706 if( aFirst->Outline()->TotalVertices() != aSecond->Outline()->TotalVertices() )
3707 return aFirst->Outline()->TotalVertices() < aSecond->Outline()->TotalVertices();
3708
3709 for( int ii = 0; ii < aFirst->Outline()->TotalVertices(); ++ii )
3710 {
3711 if( aFirst->Outline()->CVertex( ii ).x != aSecond->Outline()->CVertex( ii ).x )
3712 return aFirst->Outline()->CVertex( ii ).x < aSecond->Outline()->CVertex( ii ).x;
3713 if( aFirst->Outline()->CVertex( ii ).y != aSecond->Outline()->CVertex( ii ).y )
3714 return aFirst->Outline()->CVertex( ii ).y < aSecond->Outline()->CVertex( ii ).y;
3715 }
3716
3717 if( aFirst->m_Uuid != aSecond->m_Uuid )
3718 return aFirst->m_Uuid < aSecond->m_Uuid;
3719
3720 return aFirst < aSecond;
3721}
3722
3723
3725 int aClearance, int aMaxError, ERROR_LOC aErrorLoc,
3726 bool aSkipNPTHPadsWihNoCopper, bool aSkipPlatedPads,
3727 bool aSkipNonPlatedPads ) const
3728{
3729 auto processPad =
3730 [&]( const PAD* pad, PCB_LAYER_ID padLayer )
3731 {
3732 VECTOR2I clearance( aClearance, aClearance );
3733
3734 switch( aLayer )
3735 {
3736 case F_Cu:
3737 if( aSkipPlatedPads && pad->FlashLayer( F_Mask ) )
3738 return;
3739
3740 if( aSkipNonPlatedPads && !pad->FlashLayer( F_Mask ) )
3741 return;
3742
3743 break;
3744
3745 case B_Cu:
3746 if( aSkipPlatedPads && pad->FlashLayer( B_Mask ) )
3747 return;
3748
3749 if( aSkipNonPlatedPads && !pad->FlashLayer( B_Mask ) )
3750 return;
3751
3752 break;
3753
3754 case F_Mask:
3755 case B_Mask:
3756 clearance.x += pad->GetSolderMaskExpansion( padLayer );
3757 clearance.y += pad->GetSolderMaskExpansion( padLayer );
3758 break;
3759
3760 case F_Paste:
3761 case B_Paste:
3762 clearance += pad->GetSolderPasteMargin( padLayer );
3763 break;
3764
3765 default:
3766 break;
3767 }
3768
3769 // Our standard TransformShapeToPolygon() routines can't handle differing x:y clearance
3770 // values (which get generated when a relative paste margin is used with an oblong pad).
3771 // So we apply this huge hack and fake a larger pad to run the transform on.
3772 // Of course being a hack it falls down when dealing with custom shape pads (where the
3773 // size is only the size of the anchor), so for those we punt and just use clearance.x.
3774
3775 if( ( clearance.x < 0 || clearance.x != clearance.y )
3776 && pad->GetShape( padLayer ) != PAD_SHAPE::CUSTOM )
3777 {
3778 VECTOR2I dummySize = pad->GetSize( padLayer ) + clearance + clearance;
3779
3780 if( dummySize.x <= 0 || dummySize.y <= 0 )
3781 return;
3782
3783 PAD dummy( *pad );
3784 dummy.SetSize( padLayer, dummySize );
3785 dummy.TransformShapeToPolygon( aBuffer, padLayer, 0, aMaxError, aErrorLoc );
3786 }
3787 else
3788 {
3789 pad->TransformShapeToPolygon( aBuffer, padLayer, clearance.x, aMaxError,
3790 aErrorLoc );
3791 }
3792 };
3793
3794 for( const PAD* pad : m_pads )
3795 {
3796 if( !pad->FlashLayer( aLayer ) )
3797 continue;
3798
3799 if( aLayer == UNDEFINED_LAYER )
3800 {
3801 pad->Padstack().ForEachUniqueLayer(
3802 [&]( PCB_LAYER_ID l )
3803 {
3804 processPad( pad, l );
3805 } );
3806 }
3807 else
3808 {
3809 processPad( pad, aLayer );
3810 }
3811 }
3812}
3813
3814
3816 int aClearance, int aError, ERROR_LOC aErrorLoc,
3817 bool aIncludeText, bool aIncludeShapes,
3818 bool aIncludePrivateItems ) const
3819{
3820 std::vector<const PCB_TEXT*> texts; // List of PCB_TEXTs to convert
3821
3822 for( BOARD_ITEM* item : GraphicalItems() )
3823 {
3824 if( GetPrivateLayers().test( item->GetLayer() ) && !aIncludePrivateItems )
3825 continue;
3826
3827 if( item->Type() == PCB_TEXT_T && aIncludeText )
3828 {
3829 PCB_TEXT* text = static_cast<PCB_TEXT*>( item );
3830
3831 if( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer && text->IsVisible() )
3832 texts.push_back( text );
3833 }
3834
3835 if( item->Type() == PCB_TEXTBOX_T && aIncludeText )
3836 {
3837 PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
3838
3839 if( aLayer != UNDEFINED_LAYER && textbox->GetLayer() == aLayer && textbox->IsVisible() )
3840 {
3841 // border
3842 if( textbox->IsBorderEnabled() )
3843 textbox->PCB_SHAPE::TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
3844 // text
3845 textbox->TransformTextToPolySet( aBuffer, 0, aError, aErrorLoc );
3846 }
3847 }
3848
3849 if( item->Type() == PCB_SHAPE_T && aIncludeShapes )
3850 {
3851 const PCB_SHAPE* outline = static_cast<PCB_SHAPE*>( item );
3852
3853 if( aLayer != UNDEFINED_LAYER && outline->GetLayer() == aLayer )
3854 outline->TransformShapeToPolygon( aBuffer, aLayer, 0, aError, aErrorLoc );
3855 }
3856 }
3857
3858 if( aIncludeText )
3859 {
3860 for( const PCB_FIELD* field : m_fields )
3861 {
3862 if( field->GetLayer() == aLayer && field->IsVisible() )
3863 texts.push_back( field );
3864 }
3865 }
3866
3867 for( const PCB_TEXT* text : texts )
3868 text->TransformTextToPolySet( aBuffer, aClearance, aError, aErrorLoc );
3869}
3870
3871
3873{
3874 using OUTLINE_FONT = KIFONT::OUTLINE_FONT;
3875 using EMBEDDING_PERMISSION = OUTLINE_FONT::EMBEDDING_PERMISSION;
3876
3877 std::set<OUTLINE_FONT*> fonts;
3878
3879 for( BOARD_ITEM* item : GraphicalItems() )
3880 {
3881 if( auto* text = dynamic_cast<EDA_TEXT*>( item ) )
3882 {
3883 if( auto* font = text->GetFont(); font && !font->IsStroke() )
3884 {
3885 auto* outline = static_cast<OUTLINE_FONT*>( font );
3886 auto permission = outline->GetEmbeddingPermission();
3887
3888 if( permission == EMBEDDING_PERMISSION::EDITABLE
3889 || permission == EMBEDDING_PERMISSION::INSTALLABLE )
3890 {
3891 fonts.insert( outline );
3892 }
3893 }
3894 }
3895 }
3896
3897 for( auto* font : fonts )
3898 {
3899 auto file = GetEmbeddedFiles()->AddFile( font->GetFileName(), false );
3901 }
3902}
3903
3904
3906{
3907 if( m_componentClass )
3908 {
3909 return m_componentClass->GetFullName();
3910 }
3911
3912 return wxEmptyString;
3913}
3914
3915
3916static struct FOOTPRINT_DESC
3917{
3919 {
3921
3922 if( zcMap.Choices().GetCount() == 0 )
3923 {
3924 zcMap.Undefined( ZONE_CONNECTION::INHERITED );
3925 zcMap.Map( ZONE_CONNECTION::INHERITED, _HKI( "Inherited" ) )
3926 .Map( ZONE_CONNECTION::NONE, _HKI( "None" ) )
3927 .Map( ZONE_CONNECTION::THERMAL, _HKI( "Thermal reliefs" ) )
3928 .Map( ZONE_CONNECTION::FULL, _HKI( "Solid" ) )
3929 .Map( ZONE_CONNECTION::THT_THERMAL, _HKI( "Thermal reliefs for PTH" ) );
3930 }
3931
3933
3934 if( layerEnum.Choices().GetCount() == 0 )
3935 {
3936 layerEnum.Undefined( UNDEFINED_LAYER );
3937
3938 for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
3939 layerEnum.Map( layer, LSET::Name( layer ) );
3940 }
3941
3942 wxPGChoices fpLayers; // footprints might be placed only on F.Cu & B.Cu
3943 fpLayers.Add( LSET::Name( F_Cu ), F_Cu );
3944 fpLayers.Add( LSET::Name( B_Cu ), B_Cu );
3945
3952
3953 auto layer = new PROPERTY_ENUM<FOOTPRINT, PCB_LAYER_ID>( _HKI( "Layer" ),
3955 layer->SetChoices( fpLayers );
3956 propMgr.ReplaceProperty( TYPE_HASH( BOARD_ITEM ), _HKI( "Layer" ), layer );
3957
3958 propMgr.AddProperty( new PROPERTY<FOOTPRINT, double>( _HKI( "Orientation" ),
3960 PROPERTY_DISPLAY::PT_DEGREE ) );
3961
3962 const wxString groupFields = _HKI( "Fields" );
3963
3964 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Reference" ),
3966 groupFields );
3967 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Value" ),
3969 groupFields );
3970
3971 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Link" ),
3973 groupFields );
3974 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Library Description" ),
3976 groupFields );
3977 propMgr.AddProperty( new PROPERTY<FOOTPRINT, wxString>( _HKI( "Keywords" ),
3979 groupFields );
3980
3981 // Used only in DRC engine
3983 _HKI( "Component Class" ), NO_SETTER( FOOTPRINT, wxString ),
3987
3988 const wxString groupAttributes = _HKI( "Attributes" );
3989
3990 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Not in Schematic" ),
3991 &FOOTPRINT::SetBoardOnly, &FOOTPRINT::IsBoardOnly ), groupAttributes );
3992 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Position Files" ),
3994 groupAttributes );
3995 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Exclude From Bill of Materials" ),
3997 groupAttributes );
3998 propMgr.AddProperty( new PROPERTY<FOOTPRINT, bool>( _HKI( "Do not Populate" ),
4000 groupAttributes );
4001
4002 const wxString groupOverrides = _HKI( "Overrides" );
4003
4005 _HKI( "Exempt From Courtyard Requirement" ),
4007 groupOverrides );
4008 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>(
4009 _HKI( "Clearance Override" ),
4011 PROPERTY_DISPLAY::PT_SIZE ),
4012 groupOverrides );
4013 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<int>>(
4014 _HKI( "Solderpaste Margin Override" ),
4016 PROPERTY_DISPLAY::PT_SIZE ),
4017 groupOverrides );
4018 propMgr.AddProperty( new PROPERTY<FOOTPRINT, std::optional<double>>(
4019 _HKI( "Solderpaste Margin Ratio Override" ),
4022 PROPERTY_DISPLAY::PT_RATIO ),
4023 groupOverrides );
4025 _HKI( "Zone Connection Style" ),
4027 groupOverrides );
4028 }
const char * name
Definition: DXF_plotter.cpp:57
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...
Definition: approximation.h:32
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
constexpr int ARC_LOW_DEF
Definition: base_units.h:119
BITMAPS
A list of all bitmap identifiers.
Definition: bitmaps_list.h:33
#define DEFAULT_COURTYARD_WIDTH
BASE_SET & set(size_t pos)
Definition: base_set.h:115
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:237
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:90
PCB_LAYER_ID m_layer
Definition: board_item.h:436
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.
Definition: board_item.cpp:255
void SetX(int aX)
Definition: board_item.h:116
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:243
void SetY(int aY)
Definition: board_item.h:122
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:288
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:47
VECTOR2I GetFPRelativePosition() const
Definition: board_item.cpp:327
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:215
virtual bool IsOnCopperLayer() const
Definition: board_item.h:150
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
Definition: board_item.cpp:139
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
bool IsFootprintHolder() const
Find out if the board is being used to hold a single footprint for editing/viewing.
Definition: board.h:320
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition: board.cpp:844
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition: board.cpp:784
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
int GetTimeStamp() const
Definition: board.h:313
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 void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition: box2.h:138
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:81
const wxString & GetFullName() const
Fetches the full name of this component class.
wxString GetName() const
Fetches the display name of this component class.
EDA_ANGLE Normalize180()
Definition: eda_angle.h:260
bool IsType(FRAME_T aType) const
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual void ClearEditFlags()
Definition: eda_item.h:141
EDA_ITEM & operator=(const EDA_ITEM &aItem)
Assign the members of aItem to another object.
Definition: eda_item.cpp:261
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
const KIID m_Uuid
Definition: eda_item.h:489
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:129
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:104
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition: eda_item.h:131
virtual wxString GetClass() const =0
Return the class name.
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:130
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:213
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:286
bool IsFilled() const
Definition: eda_shape.h:98
SHAPE_T GetShape() const
Definition: eda_shape.h:132
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:210
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:408
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:377
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:269
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:291
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:400
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Loads a file from disk and adds it to the collection.
const std::map< wxString, EMBEDDED_FILE * > & EmbeddedFileMap() const
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition: property.h:669
static ENUM_MAP< T > & Instance()
Definition: property.h:663
ENUM_MAP & Undefined(T aValue)
Definition: property.h:676
wxPGChoices & Choices()
Definition: property.h:712
bool FixUuids()
Old footprints do not always have a valid UUID (some can be set to null uuid) However null UUIDs,...
Definition: footprint.cpp:691
void EmbedFonts() override
Definition: footprint.cpp:3872
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:2387
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:249
LIB_ID m_fpid
Definition: footprint.h:1062
wxString GetLibDescription() const
Definition: footprint.h:257
ZONE_CONNECTION GetLocalZoneConnection() const
Definition: footprint.h:288
const COMPONENT_CLASS * m_componentClass
Definition: footprint.h:1120
KIID_PATH m_path
Definition: footprint.h:1101
bool IsBoardOnly() const
Definition: footprint.h:750
BOX2I m_cachedTextExcludedBBox
Definition: footprint.h:1079
bool IsDNP() const
Definition: footprint.h:786
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:421
HASH_128 m_courtyard_cache_back_hash
Definition: footprint.h:1117
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
Definition: footprint.cpp:3138
bool ResolveTextVar(wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the component.
Definition: footprint.cpp:947
EDA_ANGLE GetOrientation() const
Definition: footprint.h:227
bool HasFieldByName(const wxString &aFieldName) const
Definition: footprint.cpp:577
ZONES & Zones()
Definition: footprint.h:212
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: footprint.cpp:385
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1087
ZONE_CONNECTION m_zoneConnection
Definition: footprint.h:1092
BOX2I m_cachedBoundingBox
Definition: footprint.h:1077
static double GetCoverageArea(const BOARD_ITEM *aItem, const GENERAL_COLLECTOR &aCollector)
Definition: footprint.cpp:2698
bool IsExcludedFromBOM() const
Definition: footprint.h:768
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:2457
std::optional< double > m_solderPasteMarginRatio
Definition: footprint.h:1096
PCB_FIELD * GetFieldByName(const wxString &aFieldName)
Return a field in this symbol.
Definition: footprint.cpp:588
void SetDNP(bool aDNP=true)
Definition: footprint.h:787
static bool IsLibNameValid(const wxString &aName)
Test for validity of a name of a footprint to be used in a footprint library ( no spaces,...
Definition: footprint.cpp:2255
unsigned GetPadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the number of pads.
Definition: footprint.cpp:1903
void SetLocalSolderPasteMarginRatio(std::optional< double > aRatio)
Definition: footprint.h:285
const BOX2I GetLayerBoundingBox(LSET aLayers) const
Return the bounding box of the footprint on a given set of layers.
Definition: footprint.cpp:1440
std::optional< int > m_clearance
Definition: footprint.h:1093
PCB_FIELD * AddField(const PCB_FIELD &aField)
Add a field to the symbol.
Definition: footprint.cpp:636
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.
Definition: footprint.cpp:3272
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.
Definition: footprint.cpp:3185
void SetExcludedFromBOM(bool aExclude=true)
Definition: footprint.h:769
int m_fpStatus
Definition: footprint.h:1064
PCB_FIELDS GetFields()
Return a vector of fields from the symbol.
Definition: footprint.h:716
double ViewGetLOD(int aLayer, KIGFX::VIEW *aView) const override
Return the level of detail (LOD) of the item.
Definition: footprint.cpp:2199
SHAPE_POLY_SET m_cachedHull
Definition: footprint.h:1081
std::set< wxString > GetUniquePadNumbers(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the names of the unique, non-blank pads.
Definition: footprint.cpp:1922
void SetAllowMissingCourtyard(bool aAllow=true)
Definition: footprint.h:778
void SetKeywords(const wxString &aKeywords)
Definition: footprint.h:261
int m_attributes
Definition: footprint.h:1063
PCB_LAYER_ID GetSide() const
Use instead of IsFlipped() when you also need to account for unsided footprints (those purely on user...
Definition: footprint.cpp:1657
std::vector< FP_3DMODEL > m_3D_Drawings
Definition: footprint.h:1110
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...
Definition: footprint.cpp:2791
BOARD_ITEM * DuplicateItem(const BOARD_ITEM *aItem, bool aAddToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
Definition: footprint.cpp:2496
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.
Definition: footprint.cpp:2855
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction) const override
Invoke a function on all descendants.
Definition: footprint.cpp:2080
BITMAPS GetMenuImage() const override
Return a pointer to an image to be used in menus.
Definition: footprint.cpp:2068
void RunOnDescendants(const std::function< void(BOARD_ITEM *)> &aFunction, int aDepth=0) const override
Invoke a function on all descendants.
Definition: footprint.cpp:2106
std::optional< int > GetLocalSolderPasteMargin() const
Definition: footprint.h:281
void TransformPadsToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, bool aSkipNPTHPadsWihNoCopper=false, bool aSkipPlatedPads=false, bool aSkipNonPlatedPads=false) const
Generate pads shapes on layer aLayer as polygons and adds these polygons to aBuffer.
Definition: footprint.cpp:3724
wxArrayString * m_initial_comments
Definition: footprint.h:1111
EDA_ITEM * Clone() const override
Invoke a function on all children.
Definition: footprint.cpp:2074
BOX2I GetFpPadsLocalBbox() const
Return the bounding box containing pads when the footprint is on the front side, orientation 0,...
Definition: footprint.cpp:1254
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:658
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: footprint.cpp:2293
std::optional< int > m_solderPasteMargin
Definition: footprint.h:1095
std::mutex m_courtyard_cache_mutex
Definition: footprint.h:1118
void SetExcludedFromPosFiles(bool aExclude=true)
Definition: footprint.h:760
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:239
void SetPrivateLayers(LSET aLayers)
Adds an item to the container.
Definition: footprint.h:142
void ApplyDefaultSettings(const BOARD &board, bool aStyleFields, bool aStyleText, bool aStyleShapes)
Apply default board settings to the footprint field text properties.
Definition: footprint.cpp:658
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3083
std::optional< int > GetLocalClearance() const
Definition: footprint.h:275
double Similarity(const BOARD_ITEM &aOther) const override
Return a measure of how likely the other object is to represent the same object.
Definition: footprint.cpp:3495
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:2413
FOOTPRINT & operator=(const FOOTPRINT &aOther)
Definition: footprint.cpp:823
int m_arflag
Definition: footprint.h:1106
double GetOrientationDegrees() const
Definition: footprint.h:243
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:2483
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...
Definition: footprint.cpp:1970
std::deque< PAD * > & Pads()
Definition: footprint.h:206
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: footprint.cpp:272
int GetAttributes() const
Definition: footprint.h:290
void SetLocalZoneConnection(ZONE_CONNECTION aType)
Definition: footprint.h:287
FOOTPRINT(BOARD *parent)
Definition: footprint.cpp:69
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:236
LSET GetPrivateLayers() const
Definition: footprint.h:141
wxString GetFPIDAsString() const
Definition: footprint.h:254
wxString GetValueAsString() const
Definition: footprint.h:652
bool AllowMissingCourtyard() const
Definition: footprint.h:777
wxString GetComponentClassAsString() const
Definition: footprint.cpp:3905
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
Definition: footprint.cpp:1485
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...
Definition: footprint.cpp:3815
wxString GetTypeName() const
Get the type of footprint.
Definition: footprint.cpp:1242
DRAWINGS m_drawings
Definition: footprint.h:1055
PCB_FIELD * GetFieldById(int aFieldId)
Return a field in this symbol.
Definition: footprint.cpp:566
SHAPE_POLY_SET m_courtyard_cache_back
Definition: footprint.h:1115
int m_textExcludedBBoxCacheTimeStamp
Definition: footprint.h:1080
const std::vector< wxString > & GetNetTiePadGroups() const
Definition: footprint.h:339
const LIB_ID & GetFPID() const
Definition: footprint.h:248
void SetReference(const wxString &aReference)
Definition: footprint.h:628
bool IsLocked() const override
Definition: footprint.h:411
bool IsExcludedFromPosFiles() const
Definition: footprint.h:759
void SetLayerAndFlip(PCB_LAYER_ID aLayer)
Used as Layer property setter – performs a flip if necessary to set the footprint layer.
Definition: footprint.cpp:2320
ZONES m_zones
Definition: footprint.h:1057
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with PCB_TEXTs.
Definition: footprint.cpp:615
int m_hullCacheTimeStamp
Definition: footprint.h:1082
unsigned GetUniquePadCount(INCLUDE_NPTH_T aIncludeNPTH=INCLUDE_NPTH_T(INCLUDE_NPTH)) const
Return the number of unique non-blank pads.
Definition: footprint.cpp:1953
void AddNetTiePadGroup(const wxString &aGroup)
Definition: footprint.h:346
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: footprint.cpp:2240
LSET m_privateLayers
Definition: footprint.h:1108
int GetLikelyAttribute() const
Returns the most likely attribute based on pads Either FP_THROUGH_HOLE/FP_SMD/OTHER(0)
Definition: footprint.cpp:1189
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: footprint.cpp:2283
wxString m_libDescription
Definition: footprint.h:1099
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.
Definition: footprint.cpp:1719
VECTOR2I m_pos
Definition: footprint.h:1061
std::vector< wxString > m_netTiePadGroups
Definition: footprint.h:1086
PAD * GetPad(const VECTOR2I &aPosition, LSET aLayerMask=LSET::AllLayersMask())
Get a pad at aPosition on aLayerMask in the footprint.
Definition: footprint.cpp:1871
virtual std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
Definition: footprint.cpp:2143
void Add3DModel(FP_3DMODEL *a3DModel)
Add a3DModel definition to the end of the 3D model list.
Definition: footprint.cpp:1959
void SetValue(const wxString &aValue)
Definition: footprint.h:649
GROUPS m_groups
Definition: footprint.h:1058
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.
Definition: footprint.cpp:1572
PCB_FIELD & Reference()
Definition: footprint.h:659
wxString GetReferenceAsString() const
Definition: footprint.h:631
void GetContextualTextVars(wxArrayString *aVars) const
Return the list of system text vars for this footprint.
Definition: footprint.cpp:933
std::optional< int > m_solderMaskMargin
Definition: footprint.h:1094
SHAPE_POLY_SET m_courtyard_cache_front
Definition: footprint.h:1114
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1024
std::vector< const PAD * > GetPads(const wxString &aPadNumber, const PAD *aIgnore=nullptr) const
Definition: footprint.cpp:1887
void AutoPositionFields()
Position Reference and Value fields at the top and bottom of footprint's bounding box.
Definition: footprint.cpp:2643
wxString m_keywords
Definition: footprint.h:1100
void ClearAllNets()
Clear (i.e.
Definition: footprint.cpp:1015
std::deque< PAD * > m_pads
Definition: footprint.h:1056
bool HasThroughHolePads() const
Definition: footprint.cpp:3430
void BuildCourtyardCaches(OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Build complex polygons of the courtyard areas from graphic items on the courtyard layers.
Definition: footprint.cpp:2914
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: footprint.cpp:1688
wxString GetFieldText(const wxString &aFieldName) const
Search for a field named aFieldName and returns text associated with this field.
Definition: footprint.cpp:603
EDA_ANGLE m_orient
Definition: footprint.h:1060
bool HitTestAccurate(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if a point is inside the bounding polygon of the footprint.
Definition: footprint.cpp:1794
wxString GetClass() const override
Return the class name.
Definition: footprint.h:892
void IncrementReference(int aDelta)
Bump the current reference by aDelta.
Definition: footprint.cpp:2671
std::optional< double > GetLocalSolderPasteMarginRatio() const
Definition: footprint.h:284
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition: footprint.cpp:2329
KIID m_link
Definition: footprint.h:1107
void RemoveField(const wxString &aFieldName)
Remove a user field from the footprint.
Definition: footprint.cpp:645
GROUPS & Groups()
Definition: footprint.h:215
void BuildNetTieCache()
Cache the pads that are allowed to connect to each other in the footprint.
Definition: footprint.cpp:3016
bool IsConflicting() const
Definition: footprint.cpp:927
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:220
const SHAPE_POLY_SET & GetCachedCourtyard(PCB_LAYER_ID aLayer) const
Return the cached courtyard area.
Definition: footprint.cpp:2905
PCB_FIELDS & Fields()
Definition: footprint.h:203
static const wxChar * StringLibNameInvalidChars(bool aUserReadable)
Test for validity of the name in a library of the footprint ( no spaces, dir separators ....
Definition: footprint.cpp:2266
void SetLibDescription(const wxString &aDesc)
Definition: footprint.h:258
bool TextOnly() const
Definition: footprint.cpp:1279
void CheckShortingPads(const std::function< void(const PAD *, const PAD *, int aErrorCode, const VECTOR2I &)> &aErrorHandler)
Check for overlapping, different-numbered, non-net-tie pads.
Definition: footprint.cpp:3203
double GetArea(int aPadding=0) const
Definition: footprint.cpp:1179
const wxString & GetReference() const
Definition: footprint.h:622
void CheckNetTiePadGroups(const std::function< void(const wxString &)> &aErrorHandler)
Sanity check net-tie pad groups.
Definition: footprint.cpp:3385
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Return a user-visible description string of this item.
Definition: footprint.cpp:2057
void SetBoardOnly(bool aIsBoardOnly=true)
Definition: footprint.h:751
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
Definition: footprint.h:279
PCB_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: footprint.cpp:554
timestamp_t m_lastEditTime
Definition: footprint.h:1105
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
Definition: footprint.cpp:2891
std::map< const BOARD_ITEM *, std::set< int > > m_netTieCache
Definition: footprint.h:1089
int m_fileFormatVersionAtLoad
Definition: footprint.h:1065
void SetLocalClearance(std::optional< int > aClearance)
Definition: footprint.h:276
std::optional< int > GetLocalSolderMaskMargin() const
Definition: footprint.h:278
void SetLocalSolderPasteMargin(std::optional< int > aMargin)
Definition: footprint.h:282
wxString GetKeywords() const
Definition: footprint.h:260
bool operator==(const BOARD_ITEM &aOther) const override
Definition: footprint.cpp:3442
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: footprint.h:1001
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...
Definition: footprint.cpp:3160
virtual void swapData(BOARD_ITEM *aImage) override
Definition: footprint.cpp:3408
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:2623
int m_boundingBoxCacheTimeStamp
Definition: footprint.h:1078
VECTOR2I GetPosition() const override
Definition: footprint.h:224
DRAWINGS & GraphicalItems()
Definition: footprint.h:209
PCB_FIELDS m_fields
Definition: footprint.h:1054
HASH_128 m_courtyard_cache_front_hash
Definition: footprint.h:1116
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: footprint.cpp:1787
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
Definition: footprint.cpp:1851
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: footprint.cpp:1294
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:101
double m_Opacity
Definition: footprint.h:102
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:100
VECTOR3D m_Scale
3D model scaling factor (dimensionless)
Definition: footprint.h:99
wxString m_Filename
The 3D shape filename in 3D library.
Definition: footprint.h:103
bool m_Show
Include model in rendering.
Definition: footprint.h:104
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:202
const COLLECTORS_GUIDE * GetGuide() const
Definition: collectors.h:288
Class OUTLINE_FONT implements outline font drawing.
Definition: outline_font.h:53
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:419
Definition: kiid.h:49
std::string AsStdString() const
Definition: kiid.cpp:244
wxString GetUniStringLibId() const
Definition: lib_id.h:148
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition: lib_id.h:112
const wxString GetUniStringLibNickname() const
Definition: lib_id.h:88
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllLayersMask()
Definition: lset.cpp:701
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
static LSET SideSpecificMask()
Definition: lset.cpp:785
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:410
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:183
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:385
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::...
Definition: padstack.cpp:870
@ NORMAL
Shape is the same on all layers.
@ CUSTOM
Shapes can be defined on arbitrary layers.
@ FRONT_INNER_BACK
Up to three shapes can be defined (F_Cu, inner copper layers, B_Cu)
MODE Mode() const
Definition: padstack.h:287
Definition: pad.h:54
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:439
int GetDrillSizeY() const
Definition: pad.h:311
PAD_ATTRIB GetAttribute() const
Definition: pad.h:442
const wxString & GetNumber() const
Definition: pad.h:134
VECTOR2I GetPosition() const override
Definition: pad.h:210
int GetDrillSizeX() const
Definition: pad.h:309
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1082
const PADSTACK & Padstack() const
Definition: pad.h:323
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:410
PAD_DRILL_SHAPE GetDrillShape() const
Definition: pad.h:424
const std::shared_ptr< SHAPE_POLY_SET > & GetEffectivePolygon(PCB_LAYER_ID aLayer, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Definition: pad.cpp:476
std::optional< int > GetLocalSolderMaskMargin() const
Definition: pad.h:460
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:1128
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:266
Abstract dimension API.
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: pcb_field.cpp:49
int GetId() const
Definition: pcb_field.h:107
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: pcb_field.cpp:64
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:69
virtual bool RemoveItem(BOARD_ITEM *aItem)
Remove item from group.
Definition: pcb_group.cpp:96
virtual bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:81
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
int GetWidth() const override
Definition: pcb_shape.cpp:301
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.
Definition: pcb_shape.cpp:774
STROKE_PARAMS GetStroke() const override
Definition: pcb_shape.h:89
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:69
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...
bool IsVisible() const override
Definition: pcb_textbox.h:101
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: pcb_text.cpp:133
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_text.cpp:334
PROPERTY_BASE & SetIsHiddenFromPropertiesManager(bool aHide=true)
Definition: property.h:300
PROPERTY_BASE & SetIsHiddenFromLibraryEditors(bool aIsHidden=true)
Definition: property.h:314
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:85
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:87
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
PROPERTY_BASE & ReplaceProperty(size_t aBase, const wxString &aName, PROPERTY_BASE *aNew, const wxString &aGroup=wxEmptyString)
Replace an existing property for a specific type.
void AddTypeCast(TYPE_CAST_BASE *aCast)
Register a type converter.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
double Area(bool aAbsolute=true) const
Return the area of this chain.
const VECTOR2I NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
void SetWidth(int aWidth)
Set the width of all segments in the chain.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
HASH_128 GetHash() const
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
virtual void CacheTriangulation(bool aPartition=true, bool aSimplify=false)
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
double Area()
Return the area of this poly set.
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union For aFastMode meaning, see function booleanOp.
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.
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
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(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
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 Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
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.
void Move(const VECTOR2I &aVector) override
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:42
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
Definition: stroke_params.h:89
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:133
unsigned GetAssignedPriority() const
Definition: zone.h:123
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.
Definition: convex_hull.cpp:87
#define _HKI(x)
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition: drc_item.h:52
@ DRCE_SHORTING_ITEMS
Definition: drc_item.h:40
@ DRCE_DRILLED_HOLES_COLOCATED
Definition: drc_item.h:53
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
INSPECT_RESULT
Definition: eda_item.h:43
const INSPECTOR_FUNC & INSPECTOR
Definition: eda_item.h:82
#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
#define MINIMAL_ZOOM_LEVEL_FOR_VISIBILITY
static struct FOOTPRINT_DESC _FOOTPRINT_DESC
static double polygonArea(SHAPE_POLY_SET &aPolySet)
Definition: footprint.cpp:2682
INCLUDE_NPTH_T
Definition: footprint.h:63
@ DO_NOT_INCLUDE_NPTH
Definition: footprint.h:64
@ FP_SMD
Definition: footprint.h:76
@ FP_DNP
Definition: footprint.h:83
@ FP_EXCLUDE_FROM_POS_FILES
Definition: footprint.h:77
@ FP_BOARD_ONLY
Definition: footprint.h:79
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:78
@ FP_THROUGH_HOLE
Definition: footprint.h:75
#define FP_is_PLACED
In autoplace: footprint automatically placed.
Definition: footprint.h:406
#define FP_PADS_are_LOCKED
Definition: footprint.h:408
@ FRAME_FOOTPRINT_VIEWER
Definition: frame_type.h:45
@ FRAME_FOOTPRINT_CHOOSER
Definition: frame_type.h:44
@ FRAME_FOOTPRINT_EDITOR
Definition: frame_type.h:43
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition: api_utils.cpp:26
Some functions to handle hotkeys in KiCad.
KIID niluuid(0)
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition: layer_id.cpp:208
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition: layer_ids.h:147
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:655
@ LAYER_LOCKED_ITEM_SHADOW
shadow layer for locked items
Definition: layer_ids.h:240
@ LAYER_CONFLICTS_SHADOW
shadow layer for items flagged conficting
Definition: layer_ids.h:242
@ LAYER_FOOTPRINTS_FR
show footprints on front
Definition: layer_ids.h:209
@ LAYER_FP_REFERENCES
show footprints references (when texts are visible)
Definition: layer_ids.h:212
@ LAYER_FP_TEXT
Definition: layer_ids.h:199
@ LAYER_FOOTPRINTS_BK
show footprints on back
Definition: layer_ids.h:210
@ LAYER_ANCHOR
anchor of items having an anchor point (texts, footprints)
Definition: layer_ids.h:202
@ LAYER_FP_VALUES
show footprints values (when texts are visible)
Definition: layer_ids.h:211
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ Dwgs_User
Definition: layer_ids.h:107
@ F_Paste
Definition: layer_ids.h:104
@ Cmts_User
Definition: layer_ids.h:108
@ B_Mask
Definition: layer_ids.h:98
@ B_Cu
Definition: layer_ids.h:65
@ Eco1_User
Definition: layer_ids.h:109
@ F_Mask
Definition: layer_ids.h:97
@ B_Paste
Definition: layer_ids.h:105
@ F_Fab
Definition: layer_ids.h:119
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Eco2_User
Definition: layer_ids.h:110
@ In1_Cu
Definition: layer_ids.h:66
@ B_SilkS
Definition: layer_ids.h:101
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition: mirror.h:45
FLIP_DIRECTION
Definition: mirror.h:27
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 std::optional< KICAD_T > TypeNameFromAny(const google::protobuf::Any &aMessage)
Definition: api_utils.cpp:32
KICOMMON_API VECTOR3D UnpackVector3D(const types::Vector3D &aInput)
Definition: api_utils.cpp:98
KICOMMON_API LIB_ID LibIdFromProto(const types::LibraryIdentifier &aId)
Definition: api_utils.cpp:62
KICOMMON_API types::LibraryIdentifier LibIdToProto(const LIB_ID &aId)
Definition: api_utils.cpp:68
KICOMMON_API void PackVector3D(types::Vector3D &aOutput, const VECTOR3D &aInput)
Definition: api_utils.cpp:90
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
Class to handle a set of BOARD_ITEMs.
#define TYPE_HASH(x)
Definition: property.h:71
#define NO_SETTER(owner, type)
Definition: property.h:774
#define REGISTER_TYPE(x)
Definition: property_mgr.h:371
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.
int GetTrailingInt(const wxString &aStr)
Gets the trailing int, if any, from a string.
wxString UnescapeString(const wxString &aSource)
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
bool operator()(const BOARD_ITEM *itemA, const BOARD_ITEM *itemB) const
Definition: footprint.cpp:3518
bool operator()(const PAD *aFirst, const PAD *aSecond) const
Definition: footprint.cpp:3595
bool operator()(const ZONE *aFirst, const ZONE *aSecond) const
Definition: footprint.cpp:3698
MANDATORY_FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ DATASHEET_FIELD
name of datasheet
@ FOOTPRINT_FIELD
Field Name Module PCB, i.e. "16DIP300".
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ DESCRIPTION_FIELD
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
constexpr 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:249
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:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ 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:103
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ 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:107
@ 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_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:101
@ 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_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:104
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691