1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6 * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
7 *
8 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, you may find one here:
22 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23 * or you may search the http://www.gnu.org website for the version 2 license,
24 * or you may write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 */
27
28 #include <iterator>
29 #include <drc/drc_rtree.h>
30 #include <pcb_base_frame.h>
31 #include <board_design_settings.h>
32 #include <reporter.h>
33 #include <board_commit.h>
34 #include <board.h>
35 #include <footprint.h>
36 #include <pcb_track.h>
37 #include <zone.h>
38 #include <pcb_marker.h>
39 #include <pcb_group.h>
40 #include <pcb_target.h>
41 #include <pcb_shape.h>
42 #include <pcb_text.h>
43 #include <core/arraydim.h>
44 #include <core/kicad_algo.h>
45 #include <connectivity/connectivity_data.h>
46 #include <string_utils.h>
47 #include <pgm_base.h>
48 #include <pcbnew_settings.h>
49 #include <project.h>
50 #include <project/net_settings.h>
51 #include <project/project_file.h>
52 #include <project/project_local_settings.h>
53 #include <ratsnest/ratsnest_data.h>
54 #include <tool/selection_conditions.h>
55 #include <convert_shape_list_to_polygon.h>
56 #include <wx/log.h>
57
58 // This is an odd place for this, but CvPcb won't link if it's in board_item.cpp like I first
59 // tried it.
60 wxPoint BOARD_ITEM::ZeroOffset( 0, 0 );
61
62
BOARD()63 BOARD::BOARD() :
64 BOARD_ITEM_CONTAINER( (BOARD_ITEM*) nullptr, PCB_T ),
65 m_LegacyDesignSettingsLoaded( false ),
66 m_LegacyCopperEdgeClearanceLoaded( false ),
67 m_LegacyNetclassesLoaded( false ),
68 m_boardUse( BOARD_USE::NORMAL ),
69 m_timeStamp( 1 ),
70 m_paper( PAGE_INFO::A4 ),
71 m_project( nullptr ),
72 m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ),
73 m_NetInfo( this )
74 {
75 // we have not loaded a board yet, assume latest until then.
76 m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION;
77
78 for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
79 {
80 m_layers[layer].m_name = GetStandardLayerName( ToLAYER_ID( layer ) );
81
82 if( IsCopperLayer( layer ) )
83 m_layers[layer].m_type = LT_SIGNAL;
84 else
85 m_layers[layer].m_type = LT_UNDEFINED;
86 }
87
88 BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
89
90 // Initialize default netclass.
91 NETCLASS* defaultClass = bds.GetDefault();
92 defaultClass->SetDescription( _( "This is the default net class." ) );
93
94 bds.UseCustomTrackViaSize( false );
95
96 // Initialize ratsnest
97 m_connectivity.reset( new CONNECTIVITY_DATA() );
98
99 // Set flag bits on these that will only be cleared if these are loaded from a legacy file
100 m_LegacyVisibleLayers.reset().set( Rescue );
101 m_LegacyVisibleItems.reset().set( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) );
102 }
103
104
~BOARD()105 BOARD::~BOARD()
106 {
107 // Clean up the owned elements
108 DeleteMARKERs();
109
110 for( ZONE* zone : m_zones )
111 delete zone;
112
113 m_zones.clear();
114
115 for( FOOTPRINT* footprint : m_footprints )
116 delete footprint;
117
118 m_footprints.clear();
119
120 for( PCB_TRACK* t : m_tracks )
121 delete t;
122
123 m_tracks.clear();
124
125 for( BOARD_ITEM* d : m_drawings )
126 delete d;
127
128 m_drawings.clear();
129
130 for( PCB_GROUP* g : m_groups )
131 delete g;
132
133 m_groups.clear();
134 }
135
136
BuildConnectivity(PROGRESS_REPORTER * aReporter)137 void BOARD::BuildConnectivity( PROGRESS_REPORTER* aReporter )
138 {
139 GetConnectivity()->Build( this, aReporter );
140 }
141
142
SetProject(PROJECT * aProject)143 void BOARD::SetProject( PROJECT* aProject )
144 {
145 if( m_project )
146 ClearProject();
147
148 m_project = aProject;
149
150 if( aProject )
151 {
152 PROJECT_FILE& project = aProject->GetProjectFile();
153
154 // Link the design settings object to the project file
155 project.m_BoardSettings = &GetDesignSettings();
156
157 // Set parent, which also will load the values from JSON stored in the project if we don't
158 // have legacy design settings loaded already
159 project.m_BoardSettings->SetParent( &project, !m_LegacyDesignSettingsLoaded );
160
161 // The DesignSettings' netclasses pointer will be pointing to its internal netclasses
162 // list at this point. If we loaded anything into it from a legacy board file then we
163 // want to transfer it over to the project netclasses list.
164 if( m_LegacyNetclassesLoaded )
165 project.NetSettings().m_NetClasses = GetDesignSettings().GetNetClasses();
166
167 // Now update the DesignSettings' netclass pointer to point into the project.
168 GetDesignSettings().SetNetClasses( &project.NetSettings().m_NetClasses );
169 }
170 }
171
172
ClearProject()173 void BOARD::ClearProject()
174 {
175 if( !m_project )
176 return;
177
178 PROJECT_FILE& project = m_project->GetProjectFile();
179
180 // Owned by the BOARD
181 if( project.m_BoardSettings )
182 {
183 project.ReleaseNestedSettings( project.m_BoardSettings );
184 project.m_BoardSettings = nullptr;
185 }
186
187 GetDesignSettings().SetParent( nullptr );
188 m_project = nullptr;
189 }
190
191
IncrementTimeStamp()192 void BOARD::IncrementTimeStamp()
193 {
194 m_timeStamp++;
195
196 {
197 std::unique_lock<std::mutex> cacheLock( m_CachesMutex );
198 m_InsideAreaCache.clear();
199 m_InsideCourtyardCache.clear();
200 m_InsideFCourtyardCache.clear();
201 m_InsideBCourtyardCache.clear();
202 m_LayerExpressionCache.clear();
203 }
204
205 m_CopperZoneRTrees.clear();
206 }
207
ResolveDRCExclusions()208 std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions()
209 {
210 for( PCB_MARKER* marker : GetBoard()->Markers() )
211 {
212 auto i = m_designSettings->m_DrcExclusions.find( marker->Serialize() );
213
214 if( i != m_designSettings->m_DrcExclusions.end() )
215 {
216 marker->SetExcluded( true );
217 m_designSettings->m_DrcExclusions.erase( i );
218 }
219 }
220
221 std::vector<PCB_MARKER*> newMarkers;
222
223 for( const wxString& exclusionData : m_designSettings->m_DrcExclusions )
224 {
225 PCB_MARKER* marker = PCB_MARKER::Deserialize( exclusionData );
226
227 if( marker )
228 {
229 marker->SetExcluded( true );
230 newMarkers.push_back( marker );
231 }
232 }
233
234 m_designSettings->m_DrcExclusions.clear();
235
236 return newMarkers;
237 }
238
239
ResolveTextVar(wxString * token,int aDepth) const240 bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const
241 {
242 if( GetTitleBlock().TextVarResolver( token, m_project ) )
243 {
244 return true;
245 }
246 else if( m_properties.count( *token ) )
247 {
248 *token = m_properties.at( *token );
249 return true;
250 }
251
252 return false;
253 }
254
255
GetPosition() const256 wxPoint BOARD::GetPosition() const
257 {
258 return ZeroOffset;
259 }
260
261
SetPosition(const wxPoint & aPos)262 void BOARD::SetPosition( const wxPoint& aPos )
263 {
264 wxLogWarning( wxT( "This should not be called on the BOARD object") );
265 }
266
267
Move(const wxPoint & aMoveVector)268 void BOARD::Move( const wxPoint& aMoveVector ) // overload
269 {
270 // @todo : anything like this elsewhere? maybe put into GENERAL_COLLECTOR class.
271 static const KICAD_T top_level_board_stuff[] = {
272 PCB_MARKER_T,
273 PCB_TEXT_T,
274 PCB_SHAPE_T,
275 PCB_DIM_ALIGNED_T,
276 PCB_DIM_ORTHOGONAL_T,
277 PCB_DIM_CENTER_T,
278 PCB_DIM_LEADER_T,
279 PCB_TARGET_T,
280 PCB_VIA_T,
281 PCB_TRACE_T,
282 PCB_ARC_T,
283 // PCB_PAD_T, Can't be at board level
284 // PCB_FP_TEXT_T, Can't be at board level
285 // PCB_FP_SHAPE_T, Can't be at board level
286 // PCB_FP_ZONE_T, Can't be at board level
287 PCB_FOOTPRINT_T,
288 PCB_ZONE_T,
289 EOT
290 };
291
292 INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
293 {
294 BOARD_ITEM* brd_item = (BOARD_ITEM*) item;
295
296 // aMoveVector was snapshotted, don't need "data".
297 brd_item->Move( aMoveVector );
298
299 return SEARCH_RESULT::CONTINUE;
300 };
301
302 Visit( inspector, nullptr, top_level_board_stuff );
303 }
304
305
TracksInNet(int aNetCode)306 TRACKS BOARD::TracksInNet( int aNetCode )
307 {
308 TRACKS ret;
309
310 INSPECTOR_FUNC inspector = [aNetCode, &ret]( EDA_ITEM* item, void* testData )
311 {
312 PCB_TRACK* t = static_cast<PCB_TRACK*>( item );
313
314 if( t->GetNetCode() == aNetCode )
315 ret.push_back( t );
316
317 return SEARCH_RESULT::CONTINUE;
318 };
319
320 // visit this BOARD's PCB_TRACKs and PCB_VIAs with above TRACK INSPECTOR which
321 // appends all in aNetCode to ret.
322 Visit( inspector, nullptr, GENERAL_COLLECTOR::Tracks );
323
324 return ret;
325 }
326
327
SetLayerDescr(PCB_LAYER_ID aIndex,const LAYER & aLayer)328 bool BOARD::SetLayerDescr( PCB_LAYER_ID aIndex, const LAYER& aLayer )
329 {
330 if( unsigned( aIndex ) < arrayDim( m_layers ) )
331 {
332 m_layers[ aIndex ] = aLayer;
333 return true;
334 }
335
336 return false;
337 }
338
339
GetLayerID(const wxString & aLayerName) const340 const PCB_LAYER_ID BOARD::GetLayerID( const wxString& aLayerName ) const
341 {
342
343 // Check the BOARD physical layer names.
344 for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
345 {
346 if ( ( m_layers[ layer ].m_name == aLayerName )
347 || ( m_layers[ layer ].m_userName == aLayerName ) )
348 return ToLAYER_ID( layer );
349 }
350
351 // Otherwise fall back to the system standard layer names for virtual layers.
352 for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer )
353 {
354 if( GetStandardLayerName( ToLAYER_ID( layer ) ) == aLayerName )
355 return ToLAYER_ID( layer );
356 }
357
358 return UNDEFINED_LAYER;
359 }
360
361
GetLayerName(PCB_LAYER_ID aLayer) const362 const wxString BOARD::GetLayerName( PCB_LAYER_ID aLayer ) const
363 {
364 // All layer names are stored in the BOARD.
365 if( IsLayerEnabled( aLayer ) )
366 {
367 // Standard names were set in BOARD::BOARD() but they may be over-ridden by
368 // BOARD::SetLayerName(). For copper layers, return the user defined layer name,
369 // if it was set. Otherwise return the Standard English layer name.
370 if( !m_layers[aLayer].m_userName.IsEmpty() )
371 return m_layers[aLayer].m_userName;
372 }
373
374 return GetStandardLayerName( aLayer );
375 }
376
377
SetLayerName(PCB_LAYER_ID aLayer,const wxString & aLayerName)378 bool BOARD::SetLayerName( PCB_LAYER_ID aLayer, const wxString& aLayerName )
379 {
380 wxCHECK( !aLayerName.IsEmpty(), false );
381
382 // no quote chars in the name allowed
383 if( aLayerName.Find( wxChar( '"' ) ) != wxNOT_FOUND )
384 return false;
385
386 if( IsLayerEnabled( aLayer ) )
387 {
388 m_layers[aLayer].m_userName = aLayerName;
389 return true;
390 }
391
392 return false;
393 }
394
395
GetLayerType(PCB_LAYER_ID aLayer) const396 LAYER_T BOARD::GetLayerType( PCB_LAYER_ID aLayer ) const
397 {
398 if( !IsCopperLayer( aLayer ) )
399 return LT_SIGNAL;
400
401 //@@IMB: The original test was broken due to the discontinuity
402 // in the layer sequence.
403 if( IsLayerEnabled( aLayer ) )
404 return m_layers[aLayer].m_type;
405
406 return LT_SIGNAL;
407 }
408
409
SetLayerType(PCB_LAYER_ID aLayer,LAYER_T aLayerType)410 bool BOARD::SetLayerType( PCB_LAYER_ID aLayer, LAYER_T aLayerType )
411 {
412 if( !IsCopperLayer( aLayer ) )
413 return false;
414
415 //@@IMB: The original test was broken due to the discontinuity
416 // in the layer sequence.
417 if( IsLayerEnabled( aLayer ) )
418 {
419 m_layers[aLayer].m_type = aLayerType;
420 return true;
421 }
422
423 return false;
424 }
425
426
ShowType(LAYER_T aType)427 const char* LAYER::ShowType( LAYER_T aType )
428 {
429 switch( aType )
430 {
431 default:
432 case LT_SIGNAL: return "signal";
433 case LT_POWER: return "power";
434 case LT_MIXED: return "mixed";
435 case LT_JUMPER: return "jumper";
436 }
437 }
438
439
ParseType(const char * aType)440 LAYER_T LAYER::ParseType( const char* aType )
441 {
442 if( strcmp( aType, "signal" ) == 0 )
443 return LT_SIGNAL;
444 else if( strcmp( aType, "power" ) == 0 )
445 return LT_POWER;
446 else if( strcmp( aType, "mixed" ) == 0 )
447 return LT_MIXED;
448 else if( strcmp( aType, "jumper" ) == 0 )
449 return LT_JUMPER;
450 else
451 return LT_UNDEFINED;
452 }
453
454
GetCopperLayerCount() const455 int BOARD::GetCopperLayerCount() const
456 {
457 return GetDesignSettings().GetCopperLayerCount();
458 }
459
460
SetCopperLayerCount(int aCount)461 void BOARD::SetCopperLayerCount( int aCount )
462 {
463 GetDesignSettings().SetCopperLayerCount( aCount );
464 }
465
466
GetEnabledLayers() const467 LSET BOARD::GetEnabledLayers() const
468 {
469 return GetDesignSettings().GetEnabledLayers();
470 }
471
472
IsLayerVisible(PCB_LAYER_ID aLayer) const473 bool BOARD::IsLayerVisible( PCB_LAYER_ID aLayer ) const
474 {
475 // If there is no project, assume layer is visible always
476 return GetDesignSettings().IsLayerEnabled( aLayer )
477 && ( !m_project || m_project->GetLocalSettings().m_VisibleLayers[aLayer] );
478 }
479
480
GetVisibleLayers() const481 LSET BOARD::GetVisibleLayers() const
482 {
483 return m_project ? m_project->GetLocalSettings().m_VisibleLayers : LSET::AllLayersMask();
484 }
485
486
SetEnabledLayers(LSET aLayerSet)487 void BOARD::SetEnabledLayers( LSET aLayerSet )
488 {
489 GetDesignSettings().SetEnabledLayers( aLayerSet );
490 }
491
492
IsLayerEnabled(PCB_LAYER_ID aLayer) const493 bool BOARD::IsLayerEnabled( PCB_LAYER_ID aLayer ) const
494 {
495 return GetDesignSettings().IsLayerEnabled( aLayer );
496 }
497
498
SetVisibleLayers(LSET aLayerSet)499 void BOARD::SetVisibleLayers( LSET aLayerSet )
500 {
501 if( m_project )
502 m_project->GetLocalSettings().m_VisibleLayers = aLayerSet;
503 }
504
505
SetVisibleElements(const GAL_SET & aSet)506 void BOARD::SetVisibleElements( const GAL_SET& aSet )
507 {
508 // Call SetElementVisibility for each item
509 // to ensure specific calculations that can be needed by some items,
510 // just changing the visibility flags could be not sufficient.
511 for( size_t i = 0; i < aSet.size(); i++ )
512 SetElementVisibility( GAL_LAYER_ID_START + static_cast<int>( i ), aSet[i] );
513 }
514
515
SetVisibleAlls()516 void BOARD::SetVisibleAlls()
517 {
518 SetVisibleLayers( LSET().set() );
519
520 // Call SetElementVisibility for each item,
521 // to ensure specific calculations that can be needed by some items
522 for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii )
523 SetElementVisibility( ii, true );
524 }
525
526
GetVisibleElements() const527 GAL_SET BOARD::GetVisibleElements() const
528 {
529 return m_project ? m_project->GetLocalSettings().m_VisibleItems : GAL_SET().set();
530 }
531
532
IsElementVisible(GAL_LAYER_ID aLayer) const533 bool BOARD::IsElementVisible( GAL_LAYER_ID aLayer ) const
534 {
535 return !m_project || m_project->GetLocalSettings().m_VisibleItems[aLayer - GAL_LAYER_ID_START];
536 }
537
538
SetElementVisibility(GAL_LAYER_ID aLayer,bool isEnabled)539 void BOARD::SetElementVisibility( GAL_LAYER_ID aLayer, bool isEnabled )
540 {
541 if( m_project )
542 m_project->GetLocalSettings().m_VisibleItems.set( aLayer - GAL_LAYER_ID_START, isEnabled );
543
544 switch( aLayer )
545 {
546 case LAYER_RATSNEST:
547 {
548 // because we have a tool to show/hide ratsnest relative to a pad or a footprint
549 // so the hide/show option is a per item selection
550
551 for( PCB_TRACK* track : Tracks() )
552 track->SetLocalRatsnestVisible( isEnabled );
553
554 for( FOOTPRINT* footprint : Footprints() )
555 {
556 for( PAD* pad : footprint->Pads() )
557 pad->SetLocalRatsnestVisible( isEnabled );
558 }
559
560 for( ZONE* zone : Zones() )
561 zone->SetLocalRatsnestVisible( isEnabled );
562
563 break;
564 }
565
566 default:
567 ;
568 }
569 }
570
571
IsFootprintLayerVisible(PCB_LAYER_ID aLayer) const572 bool BOARD::IsFootprintLayerVisible( PCB_LAYER_ID aLayer ) const
573 {
574 switch( aLayer )
575 {
576 case F_Cu:
577 return IsElementVisible( LAYER_MOD_FR );
578
579 case B_Cu:
580 return IsElementVisible( LAYER_MOD_BK );
581
582 default:
583 wxFAIL_MSG( wxT( "BOARD::IsModuleLayerVisible() param error: bad layer" ) );
584 return true;
585 }
586 }
587
588
589
GetDesignSettings() const590 BOARD_DESIGN_SETTINGS& BOARD::GetDesignSettings() const
591 {
592 return *m_designSettings;
593 }
594
595
GetZoneSettings() const596 const ZONE_SETTINGS& BOARD::GetZoneSettings() const
597 {
598 return GetDesignSettings().GetDefaultZoneSettings();
599 }
600
601
SetZoneSettings(const ZONE_SETTINGS & aSettings)602 void BOARD::SetZoneSettings( const ZONE_SETTINGS& aSettings )
603 {
604 GetDesignSettings().SetDefaultZoneSettings( aSettings );
605 }
606
607
Add(BOARD_ITEM * aBoardItem,ADD_MODE aMode)608 void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
609 {
610 if( aBoardItem == nullptr )
611 {
612 wxFAIL_MSG( wxT( "BOARD::Add() param error: aBoardItem nullptr" ) );
613 return;
614 }
615
616 switch( aBoardItem->Type() )
617 {
618 case PCB_NETINFO_T:
619 m_NetInfo.AppendNet( (NETINFO_ITEM*) aBoardItem );
620 break;
621
622 // this one uses a vector
623 case PCB_MARKER_T:
624 m_markers.push_back( (PCB_MARKER*) aBoardItem );
625 break;
626
627 // this one uses a vector
628 case PCB_GROUP_T:
629 m_groups.push_back( (PCB_GROUP*) aBoardItem );
630 break;
631
632 // this one uses a vector
633 case PCB_ZONE_T:
634 m_zones.push_back( (ZONE*) aBoardItem );
635 break;
636
637 case PCB_TRACE_T:
638 case PCB_VIA_T:
639 case PCB_ARC_T:
640
641 // N.B. This inserts a small memory leak as we lose the
642 if( !IsCopperLayer( aBoardItem->GetLayer() ) )
643 {
644 wxFAIL_MSG( wxT( "BOARD::Add() Cannot place Track on non-copper layer" ) );
645 return;
646 }
647
648 if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
649 m_tracks.push_back( static_cast<PCB_TRACK*>( aBoardItem ) );
650 else
651 m_tracks.push_front( static_cast<PCB_TRACK*>( aBoardItem ) );
652
653 break;
654
655 case PCB_FOOTPRINT_T:
656 if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
657 m_footprints.push_back( static_cast<FOOTPRINT*>( aBoardItem ) );
658 else
659 m_footprints.push_front( static_cast<FOOTPRINT*>( aBoardItem ) );
660
661 break;
662
663 case PCB_DIM_ALIGNED_T:
664 case PCB_DIM_CENTER_T:
665 case PCB_DIM_ORTHOGONAL_T:
666 case PCB_DIM_LEADER_T:
667 case PCB_SHAPE_T:
668 case PCB_TEXT_T:
669 case PCB_TARGET_T:
670 if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
671 m_drawings.push_back( aBoardItem );
672 else
673 m_drawings.push_front( aBoardItem );
674
675 break;
676
677 // other types may use linked list
678 default:
679 {
680 wxString msg;
681 msg.Printf( wxT( "BOARD::Add() needs work: BOARD_ITEM type (%d) not handled" ),
682 aBoardItem->Type() );
683 wxFAIL_MSG( msg );
684 return;
685 }
686 break;
687 }
688
689 aBoardItem->SetParent( this );
690 aBoardItem->ClearEditFlags();
691 m_connectivity->Add( aBoardItem );
692
693 if( aMode != ADD_MODE::BULK_INSERT && aMode != ADD_MODE::BULK_APPEND )
694 InvokeListeners( &BOARD_LISTENER::OnBoardItemAdded, *this, aBoardItem );
695 }
696
697
FinalizeBulkAdd(std::vector<BOARD_ITEM * > & aNewItems)698 void BOARD::FinalizeBulkAdd( std::vector<BOARD_ITEM*>& aNewItems )
699 {
700 InvokeListeners( &BOARD_LISTENER::OnBoardItemsAdded, *this, aNewItems );
701 }
702
703
FinalizeBulkRemove(std::vector<BOARD_ITEM * > & aRemovedItems)704 void BOARD::FinalizeBulkRemove( std::vector<BOARD_ITEM*>& aRemovedItems )
705 {
706 InvokeListeners( &BOARD_LISTENER::OnBoardItemsRemoved, *this, aRemovedItems );
707 }
708
709
Remove(BOARD_ITEM * aBoardItem,REMOVE_MODE aRemoveMode)710 void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
711 {
712 // find these calls and fix them! Don't send me no stinking' nullptr.
713 wxASSERT( aBoardItem );
714
715 switch( aBoardItem->Type() )
716 {
717 case PCB_NETINFO_T:
718 {
719 NETINFO_ITEM* item = static_cast<NETINFO_ITEM*>( aBoardItem );
720 NETINFO_ITEM* unconnected = m_NetInfo.GetNetItem( NETINFO_LIST::UNCONNECTED );
721
722 for( FOOTPRINT* fp : m_footprints )
723 {
724 for( PAD* pad : fp->Pads() )
725 {
726 if( pad->GetNet() == item )
727 pad->SetNet( unconnected );
728 }
729 }
730
731 for( ZONE* zone : m_zones )
732 {
733 if( zone->GetNet() == item )
734 zone->SetNet( unconnected );
735 }
736
737 for( PCB_TRACK* track : m_tracks )
738 {
739 if( track->GetNet() == item )
740 track->SetNet( unconnected );
741 }
742
743 m_NetInfo.RemoveNet( item );
744 break;
745 }
746
747 case PCB_MARKER_T:
748 alg::delete_matching( m_markers, aBoardItem );
749 break;
750
751 case PCB_GROUP_T:
752 alg::delete_matching( m_groups, aBoardItem );
753 break;
754
755 case PCB_ZONE_T:
756 alg::delete_matching( m_zones, aBoardItem );
757 break;
758
759 case PCB_FOOTPRINT_T:
760 alg::delete_matching( m_footprints, aBoardItem );
761 break;
762
763 case PCB_TRACE_T:
764 case PCB_ARC_T:
765 case PCB_VIA_T:
766 alg::delete_matching( m_tracks, aBoardItem );
767 break;
768
769 case PCB_DIM_ALIGNED_T:
770 case PCB_DIM_CENTER_T:
771 case PCB_DIM_ORTHOGONAL_T:
772 case PCB_DIM_LEADER_T:
773 case PCB_SHAPE_T:
774 case PCB_TEXT_T:
775 case PCB_TARGET_T:
776 alg::delete_matching( m_drawings, aBoardItem );
777 break;
778
779 // other types may use linked list
780 default:
781 wxFAIL_MSG( wxT( "BOARD::Remove() needs more ::Type() support" ) );
782 }
783
784 aBoardItem->SetFlags( STRUCT_DELETED );
785
786 PCB_GROUP* parentGroup = aBoardItem->GetParentGroup();
787
788 if( parentGroup && !( parentGroup->GetFlags() & STRUCT_DELETED ) )
789 parentGroup->RemoveItem( aBoardItem );
790
791 m_connectivity->Remove( aBoardItem );
792
793 if( aRemoveMode != REMOVE_MODE::BULK )
794 InvokeListeners( &BOARD_LISTENER::OnBoardItemRemoved, *this, aBoardItem );
795 }
796
797
GetSelectMenuText(EDA_UNITS aUnits) const798 wxString BOARD::GetSelectMenuText( EDA_UNITS aUnits ) const
799 {
800 return wxString::Format( _( "PCB" ) );
801 }
802
803
DeleteMARKERs()804 void BOARD::DeleteMARKERs()
805 {
806 // the vector does not know how to delete the PCB_MARKER, it holds pointers
807 for( PCB_MARKER* marker : m_markers )
808 delete marker;
809
810 m_markers.clear();
811 }
812
813
DeleteMARKERs(bool aWarningsAndErrors,bool aExclusions)814 void BOARD::DeleteMARKERs( bool aWarningsAndErrors, bool aExclusions )
815 {
816 // Deleting lots of items from a vector can be very slow. Copy remaining items instead.
817 MARKERS remaining;
818
819 for( PCB_MARKER* marker : m_markers )
820 {
821 if( ( marker->IsExcluded() && aExclusions )
822 || ( !marker->IsExcluded() && aWarningsAndErrors ) )
823 {
824 delete marker;
825 }
826 else
827 {
828 remaining.push_back( marker );
829 }
830 }
831
832 m_markers = remaining;
833 }
834
835
DeleteAllFootprints()836 void BOARD::DeleteAllFootprints()
837 {
838 for( FOOTPRINT* footprint : m_footprints )
839 delete footprint;
840
841 m_footprints.clear();
842 }
843
844
GetItem(const KIID & aID) const845 BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
846 {
847 if( aID == niluuid )
848 return nullptr;
849
850 for( PCB_TRACK* track : Tracks() )
851 {
852 if( track->m_Uuid == aID )
853 return track;
854 }
855
856 for( FOOTPRINT* footprint : Footprints() )
857 {
858 if( footprint->m_Uuid == aID )
859 return footprint;
860
861 for( PAD* pad : footprint->Pads() )
862 {
863 if( pad->m_Uuid == aID )
864 return pad;
865 }
866
867 if( footprint->Reference().m_Uuid == aID )
868 return &footprint->Reference();
869
870 if( footprint->Value().m_Uuid == aID )
871 return &footprint->Value();
872
873 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
874 {
875 if( drawing->m_Uuid == aID )
876 return drawing;
877 }
878
879 for( BOARD_ITEM* zone : footprint->Zones() )
880 {
881 if( zone->m_Uuid == aID )
882 return zone;
883 }
884
885 for( PCB_GROUP* group : footprint->Groups() )
886 {
887 if( group->m_Uuid == aID )
888 return group;
889 }
890 }
891
892 for( ZONE* zone : Zones() )
893 {
894 if( zone->m_Uuid == aID )
895 return zone;
896 }
897
898 for( BOARD_ITEM* drawing : Drawings() )
899 {
900 if( drawing->m_Uuid == aID )
901 return drawing;
902 }
903
904 for( PCB_MARKER* marker : m_markers )
905 {
906 if( marker->m_Uuid == aID )
907 return marker;
908 }
909
910 for( PCB_GROUP* group : m_groups )
911 {
912 if( group->m_Uuid == aID )
913 return group;
914 }
915
916 if( m_Uuid == aID )
917 return const_cast<BOARD*>( this );
918
919 // Not found; weak reference has been deleted.
920 return DELETED_BOARD_ITEM::GetInstance();
921 }
922
923
FillItemMap(std::map<KIID,EDA_ITEM * > & aMap)924 void BOARD::FillItemMap( std::map<KIID, EDA_ITEM*>& aMap )
925 {
926 // the board itself
927 aMap[ m_Uuid ] = this;
928
929 for( PCB_TRACK* track : Tracks() )
930 aMap[ track->m_Uuid ] = track;
931
932 for( FOOTPRINT* footprint : Footprints() )
933 {
934 aMap[ footprint->m_Uuid ] = footprint;
935
936 for( PAD* pad : footprint->Pads() )
937 aMap[ pad->m_Uuid ] = pad;
938
939 aMap[ footprint->Reference().m_Uuid ] = &footprint->Reference();
940 aMap[ footprint->Value().m_Uuid ] = &footprint->Value();
941
942 for( BOARD_ITEM* drawing : footprint->GraphicalItems() )
943 aMap[ drawing->m_Uuid ] = drawing;
944 }
945
946 for( ZONE* zone : Zones() )
947 aMap[ zone->m_Uuid ] = zone;
948
949 for( BOARD_ITEM* drawing : Drawings() )
950 aMap[ drawing->m_Uuid ] = drawing;
951
952 for( PCB_MARKER* marker : m_markers )
953 aMap[ marker->m_Uuid ] = marker;
954
955 for( PCB_GROUP* group : m_groups )
956 aMap[ group->m_Uuid ] = group;
957 }
958
959
ConvertCrossReferencesToKIIDs(const wxString & aSource) const960 wxString BOARD::ConvertCrossReferencesToKIIDs( const wxString& aSource ) const
961 {
962 wxString newbuf;
963 size_t sourceLen = aSource.length();
964
965 for( size_t i = 0; i < sourceLen; ++i )
966 {
967 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
968 {
969 wxString token;
970 bool isCrossRef = false;
971
972 for( i = i + 2; i < sourceLen; ++i )
973 {
974 if( aSource[i] == '}' )
975 break;
976
977 if( aSource[i] == ':' )
978 isCrossRef = true;
979
980 token.append( aSource[i] );
981 }
982
983 if( isCrossRef )
984 {
985 wxString remainder;
986 wxString ref = token.BeforeFirst( ':', &remainder );
987
988 for( const FOOTPRINT* footprint : Footprints() )
989 {
990 if( footprint->GetReference().CmpNoCase( ref ) == 0 )
991 {
992 wxString test( remainder );
993
994 if( footprint->ResolveTextVar( &test ) )
995 token = footprint->m_Uuid.AsString() + ":" + remainder;
996
997 break;
998 }
999 }
1000 }
1001
1002 newbuf.append( "${" + token + "}" );
1003 }
1004 else
1005 {
1006 newbuf.append( aSource[i] );
1007 }
1008 }
1009
1010 return newbuf;
1011 }
1012
1013
ConvertKIIDsToCrossReferences(const wxString & aSource) const1014 wxString BOARD::ConvertKIIDsToCrossReferences( const wxString& aSource ) const
1015 {
1016 wxString newbuf;
1017 size_t sourceLen = aSource.length();
1018
1019 for( size_t i = 0; i < sourceLen; ++i )
1020 {
1021 if( aSource[i] == '$' && i + 1 < sourceLen && aSource[i+1] == '{' )
1022 {
1023 wxString token;
1024 bool isCrossRef = false;
1025
1026 for( i = i + 2; i < sourceLen; ++i )
1027 {
1028 if( aSource[i] == '}' )
1029 break;
1030
1031 if( aSource[i] == ':' )
1032 isCrossRef = true;
1033
1034 token.append( aSource[i] );
1035 }
1036
1037 if( isCrossRef )
1038 {
1039 wxString remainder;
1040 wxString ref = token.BeforeFirst( ':', &remainder );
1041 BOARD_ITEM* refItem = GetItem( KIID( ref ) );
1042
1043 if( refItem && refItem->Type() == PCB_FOOTPRINT_T )
1044 token = static_cast<FOOTPRINT*>( refItem )->GetReference() + ":" + remainder;
1045 }
1046
1047 newbuf.append( "${" + token + "}" );
1048 }
1049 else
1050 {
1051 newbuf.append( aSource[i] );
1052 }
1053 }
1054
1055 return newbuf;
1056 }
1057
1058
GetNodesCount(int aNet) const1059 unsigned BOARD::GetNodesCount( int aNet ) const
1060 {
1061 unsigned retval = 0;
1062
1063 for( FOOTPRINT* footprint : Footprints() )
1064 {
1065 for( PAD* pad : footprint->Pads() )
1066 {
1067 if( ( aNet == -1 && pad->GetNetCode() > 0 ) || aNet == pad->GetNetCode() )
1068 retval++;
1069 }
1070 }
1071
1072 return retval;
1073 }
1074
1075
GetUnconnectedNetCount() const1076 unsigned BOARD::GetUnconnectedNetCount() const
1077 {
1078 return m_connectivity->GetUnconnectedCount();
1079 }
1080
1081
ComputeBoundingBox(bool aBoardEdgesOnly) const1082 EDA_RECT BOARD::ComputeBoundingBox( bool aBoardEdgesOnly ) const
1083 {
1084 EDA_RECT area;
1085 LSET visible = GetVisibleLayers();
1086 bool showInvisibleText = IsElementVisible( LAYER_MOD_TEXT_INVISIBLE )
1087 && PgmOrNull() && !PgmOrNull()->m_Printing;
1088
1089 if( aBoardEdgesOnly )
1090 visible.set( Edge_Cuts );
1091
1092 // Check shapes, dimensions, texts, and fiducials
1093 for( BOARD_ITEM* item : m_drawings )
1094 {
1095 if( aBoardEdgesOnly && ( item->GetLayer() != Edge_Cuts || item->Type() != PCB_SHAPE_T ) )
1096 continue;
1097
1098 if( ( item->GetLayerSet() & visible ).any() )
1099 area.Merge( item->GetBoundingBox() );
1100 }
1101
1102 // Check footprints
1103 for( FOOTPRINT* footprint : m_footprints )
1104 {
1105 if( !( footprint->GetLayerSet() & visible ).any() )
1106 continue;
1107
1108 if( aBoardEdgesOnly )
1109 {
1110 for( const BOARD_ITEM* edge : footprint->GraphicalItems() )
1111 {
1112 if( edge->GetLayer() == Edge_Cuts && edge->Type() == PCB_FP_SHAPE_T )
1113 area.Merge( edge->GetBoundingBox() );
1114 }
1115 }
1116 else
1117 {
1118 area.Merge( footprint->GetBoundingBox( true, showInvisibleText ) );
1119 }
1120 }
1121
1122 if( !aBoardEdgesOnly )
1123 {
1124 // Check tracks
1125 for( PCB_TRACK* track : m_tracks )
1126 {
1127 if( ( track->GetLayerSet() & visible ).any() )
1128 area.Merge( track->GetBoundingBox() );
1129 }
1130
1131 // Check zones
1132 for( ZONE* aZone : m_zones )
1133 {
1134 if( ( aZone->GetLayerSet() & visible ).any() )
1135 area.Merge( aZone->GetBoundingBox() );
1136 }
1137 }
1138
1139 return area;
1140 }
1141
1142
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)1143 void BOARD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1144 {
1145 int padCount = 0;
1146 int viaCount = 0;
1147 int trackSegmentCount = 0;
1148 std::set<int> netCodes;
1149 int unconnected = GetConnectivity()->GetUnconnectedCount();
1150
1151 for( PCB_TRACK* item : m_tracks )
1152 {
1153 if( item->Type() == PCB_VIA_T )
1154 viaCount++;
1155 else
1156 trackSegmentCount++;
1157
1158 if( item->GetNetCode() > 0 )
1159 netCodes.insert( item->GetNetCode() );
1160 }
1161
1162 for( FOOTPRINT* footprint : Footprints() )
1163 {
1164 for( PAD* pad : footprint->Pads() )
1165 {
1166 padCount++;
1167
1168 if( pad->GetNetCode() > 0 )
1169 netCodes.insert( pad->GetNetCode() );
1170 }
1171 }
1172
1173 aList.emplace_back( _( "Pads" ), wxString::Format( "%d", padCount ) );
1174 aList.emplace_back( _( "Vias" ), wxString::Format( "%d", viaCount ) );
1175 aList.emplace_back( _( "Track Segments" ), wxString::Format( "%d", trackSegmentCount ) );
1176 aList.emplace_back( _( "Nets" ), wxString::Format( "%d", (int) netCodes.size() ) );
1177 aList.emplace_back( _( "Unrouted" ), wxString::Format( "%d", unconnected ) );
1178 }
1179
1180
Visit(INSPECTOR inspector,void * testData,const KICAD_T scanTypes[])1181 SEARCH_RESULT BOARD::Visit( INSPECTOR inspector, void* testData, const KICAD_T scanTypes[] )
1182 {
1183 KICAD_T stype;
1184 SEARCH_RESULT result = SEARCH_RESULT::CONTINUE;
1185 const KICAD_T* p = scanTypes;
1186 bool done = false;
1187
1188 #if 0 && defined(DEBUG)
1189 std::cout << GetClass().mb_str() << ' ';
1190 #endif
1191
1192 while( !done )
1193 {
1194 stype = *p;
1195
1196 switch( stype )
1197 {
1198 case PCB_T:
1199 result = inspector( this, testData ); // inspect me
1200 // skip over any types handled in the above call.
1201 ++p;
1202 break;
1203
1204 /*
1205 * Instances of the requested KICAD_T live in a list, either one that I manage, or one
1206 * that my footprints manage. If it's a type managed by class FOOTPRINT, then simply
1207 * pass it on to each footprint's Visit() function via IterateForward( m_footprints, ... ).
1208 */
1209
1210 case PCB_FOOTPRINT_T:
1211 case PCB_PAD_T:
1212 case PCB_FP_TEXT_T:
1213 case PCB_FP_SHAPE_T:
1214 case PCB_FP_ZONE_T:
1215
1216 // this calls FOOTPRINT::Visit() on each footprint.
1217 result = IterateForward<FOOTPRINT*>( m_footprints, inspector, testData, p );
1218
1219 // skip over any types handled in the above call.
1220 for( ; ; )
1221 {
1222 switch( stype = *++p )
1223 {
1224 case PCB_FOOTPRINT_T:
1225 case PCB_PAD_T:
1226 case PCB_FP_TEXT_T:
1227 case PCB_FP_SHAPE_T:
1228 case PCB_FP_ZONE_T:
1229 continue;
1230
1231 default:
1232 ;
1233 }
1234
1235 break;
1236 }
1237
1238 break;
1239
1240 case PCB_SHAPE_T:
1241 case PCB_TEXT_T:
1242 case PCB_DIM_ALIGNED_T:
1243 case PCB_DIM_CENTER_T:
1244 case PCB_DIM_ORTHOGONAL_T:
1245 case PCB_DIM_LEADER_T:
1246 case PCB_TARGET_T:
1247 result = IterateForward<BOARD_ITEM*>( m_drawings, inspector, testData, p );
1248
1249 // skip over any types handled in the above call.
1250 for( ; ; )
1251 {
1252 switch( stype = *++p )
1253 {
1254 case PCB_SHAPE_T:
1255 case PCB_TEXT_T:
1256 case PCB_DIM_ALIGNED_T:
1257 case PCB_DIM_CENTER_T:
1258 case PCB_DIM_ORTHOGONAL_T:
1259 case PCB_DIM_LEADER_T:
1260 case PCB_TARGET_T:
1261 continue;
1262
1263 default:
1264 ;
1265 }
1266
1267 break;
1268 }
1269
1270 break;
1271
1272 case PCB_VIA_T:
1273 result = IterateForward<PCB_TRACK*>( m_tracks, inspector, testData, p );
1274 ++p;
1275 break;
1276
1277 case PCB_TRACE_T:
1278 case PCB_ARC_T:
1279 result = IterateForward<PCB_TRACK*>( m_tracks, inspector, testData, p );
1280 ++p;
1281 break;
1282
1283 case PCB_MARKER_T:
1284 for( PCB_MARKER* marker : m_markers )
1285 {
1286 result = marker->Visit( inspector, testData, p );
1287
1288 if( result == SEARCH_RESULT::QUIT )
1289 break;
1290 }
1291
1292 ++p;
1293 break;
1294
1295 case PCB_ZONE_T:
1296 for( ZONE* zone : m_zones)
1297 {
1298 result = zone->Visit( inspector, testData, p );
1299
1300 if( result == SEARCH_RESULT::QUIT )
1301 break;
1302 }
1303
1304 ++p;
1305 break;
1306
1307 case PCB_GROUP_T:
1308 result = IterateForward<PCB_GROUP*>( m_groups, inspector, testData, p );
1309 ++p;
1310 break;
1311
1312 default: // catch EOT or ANY OTHER type here and return.
1313 done = true;
1314 break;
1315 }
1316
1317 if( result == SEARCH_RESULT::QUIT )
1318 break;
1319 }
1320
1321 return result;
1322 }
1323
1324
FindNet(int aNetcode) const1325 NETINFO_ITEM* BOARD::FindNet( int aNetcode ) const
1326 {
1327 // the first valid netcode is 1 and the last is m_NetInfo.GetCount()-1.
1328 // zero is reserved for "no connection" and is not actually a net.
1329 // nullptr is returned for non valid netcodes
1330
1331 wxASSERT( m_NetInfo.GetNetCount() > 0 );
1332
1333 if( aNetcode == NETINFO_LIST::UNCONNECTED && m_NetInfo.GetNetCount() == 0 )
1334 return NETINFO_LIST::OrphanedItem();
1335 else
1336 return m_NetInfo.GetNetItem( aNetcode );
1337 }
1338
1339
FindNet(const wxString & aNetname) const1340 NETINFO_ITEM* BOARD::FindNet( const wxString& aNetname ) const
1341 {
1342 return m_NetInfo.GetNetItem( aNetname );
1343 }
1344
1345
FindFootprintByReference(const wxString & aReference) const1346 FOOTPRINT* BOARD::FindFootprintByReference( const wxString& aReference ) const
1347 {
1348 for( FOOTPRINT* footprint : m_footprints )
1349 {
1350 if( aReference == footprint->GetReference() )
1351 return footprint;
1352 }
1353
1354 return nullptr;
1355 }
1356
1357
FindFootprintByPath(const KIID_PATH & aPath) const1358 FOOTPRINT* BOARD::FindFootprintByPath( const KIID_PATH& aPath ) const
1359 {
1360 for( FOOTPRINT* footprint : m_footprints )
1361 {
1362 if( footprint->GetPath() == aPath )
1363 return footprint;
1364 }
1365
1366 return nullptr;
1367 }
1368
1369
GetNetClassAssignmentCandidates() const1370 std::vector<wxString> BOARD::GetNetClassAssignmentCandidates() const
1371 {
1372 std::vector<wxString> names;
1373
1374 for( const NETINFO_ITEM* net : m_NetInfo )
1375 {
1376 if( !net->GetNetname().IsEmpty() )
1377 names.emplace_back( net->GetNetname() );
1378 }
1379
1380 return names;
1381 }
1382
1383
SynchronizeProperties()1384 void BOARD::SynchronizeProperties()
1385 {
1386 if( m_project )
1387 SetProperties( m_project->GetTextVars() );
1388 }
1389
1390
SynchronizeNetsAndNetClasses()1391 void BOARD::SynchronizeNetsAndNetClasses()
1392 {
1393 if( !m_project )
1394 return;
1395
1396 NET_SETTINGS* netSettings = m_project->GetProjectFile().m_NetSettings.get();
1397 NETCLASSES& netClasses = netSettings->m_NetClasses;
1398 NETCLASSPTR defaultNetClass = netClasses.GetDefault();
1399
1400 for( NETINFO_ITEM* net : m_NetInfo )
1401 {
1402 const wxString& netname = net->GetNetname();
1403 const wxString& netclassName = netSettings->GetNetclassName( netname );
1404
1405 net->SetNetClass( netClasses.Find( netclassName ) );
1406 }
1407
1408 BOARD_DESIGN_SETTINGS& bds = GetDesignSettings();
1409
1410 // Set initial values for custom track width & via size to match the default
1411 // netclass settings
1412 bds.UseCustomTrackViaSize( false );
1413 bds.SetCustomTrackWidth( defaultNetClass->GetTrackWidth() );
1414 bds.SetCustomViaSize( defaultNetClass->GetViaDiameter() );
1415 bds.SetCustomViaDrill( defaultNetClass->GetViaDrill() );
1416 bds.SetCustomDiffPairWidth( defaultNetClass->GetDiffPairWidth() );
1417 bds.SetCustomDiffPairGap( defaultNetClass->GetDiffPairGap() );
1418 bds.SetCustomDiffPairViaGap( defaultNetClass->GetDiffPairViaGap() );
1419
1420 InvokeListeners( &BOARD_LISTENER::OnBoardNetSettingsChanged, *this );
1421 }
1422
1423
SetAreasNetCodesFromNetNames()1424 int BOARD::SetAreasNetCodesFromNetNames()
1425 {
1426 int error_count = 0;
1427
1428 for( ZONE* zone : Zones() )
1429 {
1430 if( !zone->IsOnCopperLayer() )
1431 {
1432 zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
1433 continue;
1434 }
1435
1436 if( zone->GetNetCode() != 0 ) // i.e. if this zone is connected to a net
1437 {
1438 const NETINFO_ITEM* net = zone->GetNet();
1439
1440 if( net )
1441 {
1442 zone->SetNetCode( net->GetNetCode() );
1443 }
1444 else
1445 {
1446 error_count++;
1447
1448 // keep Net Name and set m_NetCode to -1 : error flag.
1449 zone->SetNetCode( -1 );
1450 }
1451 }
1452 }
1453
1454 return error_count;
1455 }
1456
1457
GetPad(const wxPoint & aPosition,LSET aLayerSet) const1458 PAD* BOARD::GetPad( const wxPoint& aPosition, LSET aLayerSet ) const
1459 {
1460 if( !aLayerSet.any() )
1461 aLayerSet = LSET::AllCuMask();
1462
1463 for( FOOTPRINT* footprint : m_footprints )
1464 {
1465 PAD* pad = nullptr;
1466
1467 if( footprint->HitTest( aPosition ) )
1468 pad = footprint->GetPad( aPosition, aLayerSet );
1469
1470 if( pad )
1471 return pad;
1472 }
1473
1474 return nullptr;
1475 }
1476
1477
GetPad(const PCB_TRACK * aTrace,ENDPOINT_T aEndPoint) const1478 PAD* BOARD::GetPad( const PCB_TRACK* aTrace, ENDPOINT_T aEndPoint ) const
1479 {
1480 const wxPoint& aPosition = aTrace->GetEndPoint( aEndPoint );
1481
1482 LSET lset( aTrace->GetLayer() );
1483
1484 return GetPad( aPosition, lset );
1485 }
1486
1487
GetPadFast(const wxPoint & aPosition,LSET aLayerSet) const1488 PAD* BOARD::GetPadFast( const wxPoint& aPosition, LSET aLayerSet ) const
1489 {
1490 for( FOOTPRINT* footprint : Footprints() )
1491 {
1492 for( PAD* pad : footprint->Pads() )
1493 {
1494 if( pad->GetPosition() != aPosition )
1495 continue;
1496
1497 // Pad found, it must be on the correct layer
1498 if( ( pad->GetLayerSet() & aLayerSet ).any() )
1499 return pad;
1500 }
1501 }
1502
1503 return nullptr;
1504 }
1505
1506
GetPad(std::vector<PAD * > & aPadList,const wxPoint & aPosition,LSET aLayerSet) const1507 PAD* BOARD::GetPad( std::vector<PAD*>& aPadList, const wxPoint& aPosition, LSET aLayerSet ) const
1508 {
1509 // Search aPadList for aPosition
1510 // aPadList is sorted by X then Y values, and a fast binary search is used
1511 int idxmax = aPadList.size() - 1;
1512
1513 int delta = aPadList.size();
1514
1515 int idx = 0; // Starting index is the beginning of list
1516
1517 while( delta )
1518 {
1519 // Calculate half size of remaining interval to test.
1520 // Ensure the computed value is not truncated (too small)
1521 if( (delta & 1) && ( delta > 1 ) )
1522 delta++;
1523
1524 delta /= 2;
1525
1526 PAD* pad = aPadList[idx];
1527
1528 if( pad->GetPosition() == aPosition ) // candidate found
1529 {
1530 // The pad must match the layer mask:
1531 if( ( aLayerSet & pad->GetLayerSet() ).any() )
1532 return pad;
1533
1534 // More than one pad can be at aPosition
1535 // search for a pad at aPosition that matched this mask
1536
1537 // search next
1538 for( int ii = idx+1; ii <= idxmax; ii++ )
1539 {
1540 pad = aPadList[ii];
1541
1542 if( pad->GetPosition() != aPosition )
1543 break;
1544
1545 if( ( aLayerSet & pad->GetLayerSet() ).any() )
1546 return pad;
1547 }
1548 // search previous
1549 for( int ii = idx - 1 ;ii >=0; ii-- )
1550 {
1551 pad = aPadList[ii];
1552
1553 if( pad->GetPosition() != aPosition )
1554 break;
1555
1556 if( ( aLayerSet & pad->GetLayerSet() ).any() )
1557 return pad;
1558 }
1559
1560 // Not found:
1561 return nullptr;
1562 }
1563
1564 if( pad->GetPosition().x == aPosition.x ) // Must search considering Y coordinate
1565 {
1566 if( pad->GetPosition().y < aPosition.y ) // Must search after this item
1567 {
1568 idx += delta;
1569
1570 if( idx > idxmax )
1571 idx = idxmax;
1572 }
1573 else // Must search before this item
1574 {
1575 idx -= delta;
1576
1577 if( idx < 0 )
1578 idx = 0;
1579 }
1580 }
1581 else if( pad->GetPosition().x < aPosition.x ) // Must search after this item
1582 {
1583 idx += delta;
1584
1585 if( idx > idxmax )
1586 idx = idxmax;
1587 }
1588 else // Must search before this item
1589 {
1590 idx -= delta;
1591
1592 if( idx < 0 )
1593 idx = 0;
1594 }
1595 }
1596
1597 return nullptr;
1598 }
1599
1600
1601 /**
1602 * Used by #GetSortedPadListByXCoord to sort a pad list by X coordinate value.
1603 *
1604 * This function is used to build ordered pads lists
1605 */
sortPadsByXthenYCoord(PAD * const & aLH,PAD * const & aRH)1606 bool sortPadsByXthenYCoord( PAD* const & aLH, PAD* const & aRH )
1607 {
1608 if( aLH->GetPosition().x == aRH->GetPosition().x )
1609 return aLH->GetPosition().y < aRH->GetPosition().y;
1610
1611 return aLH->GetPosition().x < aRH->GetPosition().x;
1612 }
1613
1614
GetSortedPadListByXthenYCoord(std::vector<PAD * > & aVector,int aNetCode) const1615 void BOARD::GetSortedPadListByXthenYCoord( std::vector<PAD*>& aVector, int aNetCode ) const
1616 {
1617 for( FOOTPRINT* footprint : Footprints() )
1618 {
1619 for( PAD* pad : footprint->Pads( ) )
1620 {
1621 if( aNetCode < 0 || pad->GetNetCode() == aNetCode )
1622 aVector.push_back( pad );
1623 }
1624 }
1625
1626 std::sort( aVector.begin(), aVector.end(), sortPadsByXthenYCoord );
1627 }
1628
1629
PadDelete(PAD * aPad)1630 void BOARD::PadDelete( PAD* aPad )
1631 {
1632 GetConnectivity()->Remove( aPad );
1633
1634 InvokeListeners( &BOARD_LISTENER::OnBoardItemRemoved, *this, aPad );
1635
1636 aPad->DeleteStructure();
1637 }
1638
1639
GetTrackLength(const PCB_TRACK & aTrack) const1640 std::tuple<int, double, double> BOARD::GetTrackLength( const PCB_TRACK& aTrack ) const
1641 {
1642 int count = 0;
1643 double length = 0.0;
1644 double package_length = 0.0;
1645
1646 constexpr KICAD_T types[] = { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, EOT };
1647 auto connectivity = GetBoard()->GetConnectivity();
1648 BOARD_STACKUP& stackup = GetDesignSettings().GetStackupDescriptor();
1649 bool useHeight = GetDesignSettings().m_UseHeightForLengthCalcs;
1650
1651 for( BOARD_CONNECTED_ITEM* item : connectivity->GetConnectedItems(
1652 static_cast<const BOARD_CONNECTED_ITEM*>( &aTrack ), types ) )
1653 {
1654 count++;
1655
1656 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1657 {
1658 if( track->Type() == PCB_VIA_T && useHeight )
1659 {
1660 PCB_VIA* via = static_cast<PCB_VIA*>( track );
1661 length += stackup.GetLayerDistance( via->TopLayer(), via->BottomLayer() );
1662 continue;
1663 }
1664 else if( track->Type() == PCB_ARC_T )
1665 {
1666 // Note: we don't apply the clip-to-pad optimization if an arc ends in a pad
1667 // Room for future improvement.
1668 length += track->GetLength();
1669 continue;
1670 }
1671
1672 bool inPad = false;
1673 SEG trackSeg( track->GetStart(), track->GetEnd() );
1674 double segLen = trackSeg.Length();
1675 double segInPadLen = 0;
1676
1677 for( auto pad_it : connectivity->GetConnectedPads( item ) )
1678 {
1679 PAD* pad = static_cast<PAD*>( pad_it );
1680
1681 bool hitStart = pad->HitTest( track->GetStart(), track->GetWidth() / 2 );
1682 bool hitEnd = pad->HitTest( track->GetEnd(), track->GetWidth() / 2 );
1683
1684 if( hitStart && hitEnd )
1685 {
1686 inPad = true;
1687 break;
1688 }
1689 else if( hitStart || hitEnd )
1690 {
1691 VECTOR2I loc;
1692
1693 // We may not collide even if we passed the bounding-box hit test
1694 if( pad->GetEffectivePolygon()->Collide( trackSeg, 0, nullptr, &loc ) )
1695 {
1696 // Part 1: length of the seg to the intersection with the pad poly
1697 if( hitStart )
1698 trackSeg.A = loc;
1699 else
1700 trackSeg.B = loc;
1701
1702 segLen = trackSeg.Length();
1703
1704 // Part 2: length from the intersection to the pad anchor
1705 segInPadLen += ( loc - pad->GetPosition() ).EuclideanNorm();
1706 }
1707 }
1708 }
1709
1710 if( !inPad )
1711 length += segLen + segInPadLen;
1712 }
1713 else if( PAD* pad = dyn_cast<PAD*>( item ) )
1714 {
1715 package_length += pad->GetPadToDieLength();
1716 }
1717 }
1718
1719 return std::make_tuple( count, length, package_length );
1720 }
1721
1722
GetFootprint(const wxPoint & aPosition,PCB_LAYER_ID aActiveLayer,bool aVisibleOnly,bool aIgnoreLocked) const1723 FOOTPRINT* BOARD::GetFootprint( const wxPoint& aPosition, PCB_LAYER_ID aActiveLayer,
1724 bool aVisibleOnly, bool aIgnoreLocked ) const
1725 {
1726 FOOTPRINT* footprint = nullptr;
1727 FOOTPRINT* alt_footprint = nullptr;
1728 int min_dim = 0x7FFFFFFF;
1729 int alt_min_dim = 0x7FFFFFFF;
1730 bool current_layer_back = IsBackLayer( aActiveLayer );
1731
1732 for( FOOTPRINT* candidate : m_footprints )
1733 {
1734 // is the ref point within the footprint's bounds?
1735 if( !candidate->HitTest( aPosition ) )
1736 continue;
1737
1738 // if caller wants to ignore locked footprints, and this one is locked, skip it.
1739 if( aIgnoreLocked && candidate->IsLocked() )
1740 continue;
1741
1742 PCB_LAYER_ID layer = candidate->GetLayer();
1743
1744 // Filter non visible footprints if requested
1745 if( !aVisibleOnly || IsFootprintLayerVisible( layer ) )
1746 {
1747 EDA_RECT bb = candidate->GetBoundingBox( false, false );
1748
1749 int offx = bb.GetX() + bb.GetWidth() / 2;
1750 int offy = bb.GetY() + bb.GetHeight() / 2;
1751
1752 // off x & offy point to the middle of the box.
1753 int dist = ( aPosition.x - offx ) * ( aPosition.x - offx ) +
1754 ( aPosition.y - offy ) * ( aPosition.y - offy );
1755
1756 if( current_layer_back == IsBackLayer( layer ) )
1757 {
1758 if( dist <= min_dim )
1759 {
1760 // better footprint shown on the active side
1761 footprint = candidate;
1762 min_dim = dist;
1763 }
1764 }
1765 else if( aVisibleOnly && IsFootprintLayerVisible( layer ) )
1766 {
1767 if( dist <= alt_min_dim )
1768 {
1769 // better footprint shown on the other side
1770 alt_footprint = candidate;
1771 alt_min_dim = dist;
1772 }
1773 }
1774 }
1775 }
1776
1777 if( footprint )
1778 return footprint;
1779
1780 if( alt_footprint)
1781 return alt_footprint;
1782
1783 return nullptr;
1784 }
1785
1786
GetZoneList(bool aIncludeZonesInFootprints) const1787 std::list<ZONE*> BOARD::GetZoneList( bool aIncludeZonesInFootprints ) const
1788 {
1789 std::list<ZONE*> zones;
1790
1791 for( ZONE* zone : Zones() )
1792 zones.push_back( zone );
1793
1794 if( aIncludeZonesInFootprints )
1795 {
1796 for( FOOTPRINT* footprint : m_footprints )
1797 {
1798 for( FP_ZONE* zone : footprint->Zones() )
1799 zones.push_back( zone );
1800 }
1801 }
1802
1803 return zones;
1804 }
1805
1806
AddArea(PICKED_ITEMS_LIST * aNewZonesList,int aNetcode,PCB_LAYER_ID aLayer,wxPoint aStartPointPosition,ZONE_BORDER_DISPLAY_STYLE aHatch)1807 ZONE* BOARD::AddArea( PICKED_ITEMS_LIST* aNewZonesList, int aNetcode, PCB_LAYER_ID aLayer,
1808 wxPoint aStartPointPosition, ZONE_BORDER_DISPLAY_STYLE aHatch )
1809 {
1810 ZONE* new_area = new ZONE( this );
1811
1812 new_area->SetNetCode( aNetcode );
1813 new_area->SetLayer( aLayer );
1814
1815 m_zones.push_back( new_area );
1816
1817 new_area->SetHatchStyle( (ZONE_BORDER_DISPLAY_STYLE) aHatch );
1818
1819 // Add the first corner to the new zone
1820 new_area->AppendCorner( aStartPointPosition, -1 );
1821
1822 if( aNewZonesList )
1823 {
1824 ITEM_PICKER picker( nullptr, new_area, UNDO_REDO::NEWITEM );
1825 aNewZonesList->PushItem( picker );
1826 }
1827
1828 return new_area;
1829 }
1830
1831
NormalizeAreaPolygon(PICKED_ITEMS_LIST * aNewZonesList,ZONE * aCurrArea)1832 bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE* aCurrArea )
1833 {
1834 // mark all areas as unmodified except this one, if modified
1835 for( ZONE* zone : m_zones )
1836 zone->SetLocalFlags( 0 );
1837
1838 aCurrArea->SetLocalFlags( 1 );
1839
1840 if( aCurrArea->Outline()->IsSelfIntersecting() )
1841 {
1842 aCurrArea->UnHatchBorder();
1843
1844 // Normalize copied area and store resulting number of polygons
1845 int n_poly = aCurrArea->Outline()->NormalizeAreaOutlines();
1846
1847 // If clipping has created some polygons, we must add these new copper areas.
1848 if( n_poly > 1 )
1849 {
1850 ZONE* NewArea;
1851
1852 // Move the newly created polygons to new areas, removing them from the current area
1853 for( int ip = 1; ip < n_poly; ip++ )
1854 {
1855 // Create new copper area and copy poly into it
1856 SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( ip ) );
1857 NewArea = AddArea( aNewZonesList, aCurrArea->GetNetCode(), aCurrArea->GetLayer(),
1858 wxPoint(0, 0), aCurrArea->GetHatchStyle() );
1859
1860 // remove the poly that was automatically created for the new area
1861 // and replace it with a poly from NormalizeAreaOutlines
1862 delete NewArea->Outline();
1863 NewArea->SetOutline( new_p );
1864 NewArea->HatchBorder();
1865 NewArea->SetLocalFlags( 1 );
1866 }
1867
1868 SHAPE_POLY_SET* new_p = new SHAPE_POLY_SET( aCurrArea->Outline()->UnitSet( 0 ) );
1869 delete aCurrArea->Outline();
1870 aCurrArea->SetOutline( new_p );
1871 }
1872 }
1873
1874 aCurrArea->HatchBorder();
1875
1876 return true;
1877 }
1878
1879
GetBoardPolygonOutlines(SHAPE_POLY_SET & aOutlines,OUTLINE_ERROR_HANDLER * aErrorHandler)1880 bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
1881 OUTLINE_ERROR_HANDLER* aErrorHandler )
1882 {
1883 int chainingEpsilon = Millimeter2iu( 0.02 ); // max dist from one endPt to next startPt
1884
1885 bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
1886 chainingEpsilon, aErrorHandler );
1887
1888 // Make polygon strictly simple to avoid issues (especially in 3D viewer)
1889 aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
1890
1891 return success;
1892 }
1893
1894
GetPads() const1895 const std::vector<PAD*> BOARD::GetPads() const
1896 {
1897 std::vector<PAD*> allPads;
1898
1899 for( FOOTPRINT* footprint : Footprints() )
1900 {
1901 for( PAD* pad : footprint->Pads() )
1902 allPads.push_back( pad );
1903 }
1904
1905 return allPads;
1906 }
1907
1908
AllConnectedItems()1909 const std::vector<BOARD_CONNECTED_ITEM*> BOARD::AllConnectedItems()
1910 {
1911 std::vector<BOARD_CONNECTED_ITEM*> items;
1912
1913 for( PCB_TRACK* track : Tracks() )
1914 items.push_back( track );
1915
1916 for( FOOTPRINT* footprint : Footprints() )
1917 {
1918 for( PAD* pad : footprint->Pads() )
1919 items.push_back( pad );
1920 }
1921
1922 for( ZONE* zone : Zones() )
1923 items.push_back( zone );
1924
1925 return items;
1926 }
1927
1928
ClearAllNetCodes()1929 void BOARD::ClearAllNetCodes()
1930 {
1931 for( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
1932 item->SetNetCode( 0 );
1933 }
1934
1935
MapNets(const BOARD * aDestBoard)1936 void BOARD::MapNets( const BOARD* aDestBoard )
1937 {
1938 for( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
1939 {
1940 NETINFO_ITEM* netInfo = aDestBoard->FindNet( item->GetNetname() );
1941
1942 if( netInfo )
1943 item->SetNet( netInfo );
1944 else
1945 item->SetNetCode( 0 );
1946 }
1947 }
1948
1949
SanitizeNetcodes()1950 void BOARD::SanitizeNetcodes()
1951 {
1952 for ( BOARD_CONNECTED_ITEM* item : AllConnectedItems() )
1953 {
1954 if( FindNet( item->GetNetCode() ) == nullptr )
1955 item->SetNetCode( NETINFO_LIST::ORPHANED );
1956 }
1957 }
1958
1959
AddListener(BOARD_LISTENER * aListener)1960 void BOARD::AddListener( BOARD_LISTENER* aListener )
1961 {
1962 if( !alg::contains( m_listeners, aListener ) )
1963 m_listeners.push_back( aListener );
1964 }
1965
1966
RemoveListener(BOARD_LISTENER * aListener)1967 void BOARD::RemoveListener( BOARD_LISTENER* aListener )
1968 {
1969 auto i = std::find( m_listeners.begin(), m_listeners.end(), aListener );
1970
1971 if( i != m_listeners.end() )
1972 {
1973 std::iter_swap( i, m_listeners.end() - 1 );
1974 m_listeners.pop_back();
1975 }
1976 }
1977
1978
OnItemChanged(BOARD_ITEM * aItem)1979 void BOARD::OnItemChanged( BOARD_ITEM* aItem )
1980 {
1981 InvokeListeners( &BOARD_LISTENER::OnBoardItemChanged, *this, aItem );
1982 }
1983
1984
OnItemsChanged(std::vector<BOARD_ITEM * > & aItems)1985 void BOARD::OnItemsChanged( std::vector<BOARD_ITEM*>& aItems )
1986 {
1987 InvokeListeners( &BOARD_LISTENER::OnBoardItemsChanged, *this, aItems );
1988 }
1989
1990
ResetNetHighLight()1991 void BOARD::ResetNetHighLight()
1992 {
1993 m_highLight.Clear();
1994 m_highLightPrevious.Clear();
1995
1996 InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
1997 }
1998
1999
SetHighLightNet(int aNetCode,bool aMulti)2000 void BOARD::SetHighLightNet( int aNetCode, bool aMulti )
2001 {
2002 if( !m_highLight.m_netCodes.count( aNetCode ) )
2003 {
2004 if( !aMulti )
2005 m_highLight.m_netCodes.clear();
2006
2007 m_highLight.m_netCodes.insert( aNetCode );
2008 InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
2009 }
2010 }
2011
2012
HighLightON(bool aValue)2013 void BOARD::HighLightON( bool aValue )
2014 {
2015 if( m_highLight.m_highLightOn != aValue )
2016 {
2017 m_highLight.m_highLightOn = aValue;
2018 InvokeListeners( &BOARD_LISTENER::OnBoardHighlightNetChanged, *this );
2019 }
2020 }
2021
2022
GroupsSanityCheck(bool repair)2023 wxString BOARD::GroupsSanityCheck( bool repair )
2024 {
2025 if( repair )
2026 {
2027 while( GroupsSanityCheckInternal( repair ) != wxEmptyString )
2028 {};
2029
2030 return wxEmptyString;
2031 }
2032 return GroupsSanityCheckInternal( repair );
2033 }
2034
2035
GroupsSanityCheckInternal(bool repair)2036 wxString BOARD::GroupsSanityCheckInternal( bool repair )
2037 {
2038 // Cycle detection
2039 //
2040 // Each group has at most one parent group.
2041 // So we start at group 0 and traverse the parent chain, marking groups seen along the way.
2042 // If we ever see a group that we've already marked, that's a cycle.
2043 // If we reach the end of the chain, we know all groups in that chain are not part of any cycle.
2044 //
2045 // Algorithm below is linear in the # of groups because each group is visited only once.
2046 // There may be extra time taken due to the container access calls and iterators.
2047 //
2048 // Groups we know are cycle free
2049 std::unordered_set<PCB_GROUP*> knownCycleFreeGroups;
2050 // Groups in the current chain we're exploring.
2051 std::unordered_set<PCB_GROUP*> currentChainGroups;
2052 // Groups we haven't checked yet.
2053 std::unordered_set<PCB_GROUP*> toCheckGroups;
2054
2055 // Initialize set of groups to check that could participate in a cycle.
2056 for( PCB_GROUP* group : Groups() )
2057 toCheckGroups.insert( group);
2058
2059 while( !toCheckGroups.empty() )
2060 {
2061 currentChainGroups.clear();
2062 PCB_GROUP* group = *toCheckGroups.begin();
2063
2064 while( true )
2065 {
2066 if( currentChainGroups.find( group ) != currentChainGroups.end() )
2067 {
2068 if( repair )
2069 Remove( group );
2070
2071 return "Cycle detected in group membership";
2072 }
2073 else if( knownCycleFreeGroups.find( group ) != knownCycleFreeGroups.end() )
2074 {
2075 // Parent is a group we know does not lead to a cycle
2076 break;
2077 }
2078
2079 currentChainGroups.insert( group );
2080 // We haven't visited currIdx yet, so it must be in toCheckGroups
2081 toCheckGroups.erase( group );
2082
2083 group = group->GetParentGroup();
2084
2085 if( !group )
2086 {
2087 // end of chain and no cycles found in this chain
2088 break;
2089 }
2090 }
2091
2092 // No cycles found in chain, so add it to set of groups we know don't participate
2093 // in a cycle.
2094 knownCycleFreeGroups.insert( currentChainGroups.begin(), currentChainGroups.end() );
2095 }
2096
2097 // Success
2098 return "";
2099 }
2100
2101
GroupLegalOps(const PCB_SELECTION & selection) const2102 BOARD::GroupLegalOpsField BOARD::GroupLegalOps( const PCB_SELECTION& selection ) const
2103 {
2104 bool hasGroup = false;
2105 bool hasMember = false;
2106
2107 for( EDA_ITEM* item : selection )
2108 {
2109 if( item->Type() == PCB_GROUP_T )
2110 hasGroup = true;
2111
2112 if( static_cast<BOARD_ITEM*>( item )->GetParentGroup() )
2113 hasMember = true;
2114 }
2115
2116 GroupLegalOpsField legalOps;
2117
2118 legalOps.create = true;
2119 legalOps.removeItems = hasMember;
2120 legalOps.ungroup = hasGroup;
2121 legalOps.enter = hasGroup && selection.Size() == 1;
2122
2123 return legalOps;
2124 }
2125
2126
operator ()(const BOARD_ITEM * a,const BOARD_ITEM * b) const2127 bool BOARD::cmp_items::operator() ( const BOARD_ITEM* a, const BOARD_ITEM* b ) const
2128 {
2129 if( a->Type() != b->Type() )
2130 return a->Type() < b->Type();
2131
2132 if( a->GetLayer() != b->GetLayer() )
2133 return a->GetLayer() < b->GetLayer();
2134
2135 if( a->GetPosition().x != b->GetPosition().x )
2136 return a->GetPosition().x < b->GetPosition().x;
2137
2138 if( a->GetPosition().y != b->GetPosition().y )
2139 return a->GetPosition().y < b->GetPosition().y;
2140
2141 if( a->m_Uuid != b->m_Uuid ) // shopuld be always the case foer valid boards
2142 return a->m_Uuid < b->m_Uuid;
2143
2144 return a < b;
2145 }
2146
2147
operator ()(const BOARD_ITEM * aFirst,const BOARD_ITEM * aSecond) const2148 bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
2149 const BOARD_ITEM* aSecond ) const
2150 {
2151 if( aFirst->Type() != aSecond->Type() )
2152 return aFirst->Type() < aSecond->Type();
2153
2154 if( aFirst->GetLayer() != aSecond->GetLayer() )
2155 return aFirst->GetLayer() < aSecond->GetLayer();
2156
2157 if( aFirst->Type() == PCB_SHAPE_T )
2158 {
2159 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aFirst );
2160 const PCB_SHAPE* other = static_cast<const PCB_SHAPE*>( aSecond );
2161 return shape->Compare( other );
2162 }
2163 else if( aFirst->Type() == PCB_TEXT_T )
2164 {
2165 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aFirst );
2166 const PCB_TEXT* other = static_cast<const PCB_TEXT*>( aSecond );
2167 return text->Compare( other );
2168 }
2169
2170 return aFirst->m_Uuid < aSecond->m_Uuid;
2171 }
2172
2173
ConvertBrdLayerToPolygonalContours(PCB_LAYER_ID aLayer,SHAPE_POLY_SET & aOutlines) const2174 void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
2175 SHAPE_POLY_SET& aOutlines ) const
2176 {
2177 int maxError = GetDesignSettings().m_MaxError;
2178
2179 // convert tracks and vias:
2180 for( const PCB_TRACK* track : m_tracks )
2181 {
2182 if( !track->IsOnLayer( aLayer ) )
2183 continue;
2184
2185 track->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
2186 ERROR_INSIDE );
2187 }
2188
2189 // convert pads and other copper items in footprints
2190 for( const FOOTPRINT* footprint : m_footprints )
2191 {
2192 footprint->TransformPadsWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
2193 ERROR_INSIDE );
2194
2195 // Micro-wave footprints may have items on copper layers
2196 footprint->TransformFPShapesWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
2197 ERROR_INSIDE,
2198 true, /* include text */
2199 true /* include shapes */ );
2200
2201 for( const ZONE* zone : footprint->Zones() )
2202 {
2203 if( zone->GetLayerSet().test( aLayer ) )
2204 zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
2205 }
2206 }
2207
2208 // convert copper zones
2209 for( const ZONE* zone : Zones() )
2210 {
2211 if( zone->GetLayerSet().test( aLayer ) )
2212 zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
2213 }
2214
2215 // convert graphic items on copper layers (texts)
2216 for( const BOARD_ITEM* item : m_drawings )
2217 {
2218 if( !item->IsOnLayer( aLayer ) )
2219 continue;
2220
2221 switch( item->Type() )
2222 {
2223 case PCB_SHAPE_T:
2224 {
2225 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( item );
2226 shape->TransformShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
2227 ERROR_INSIDE );
2228 break;
2229 }
2230
2231 case PCB_TEXT_T:
2232 {
2233 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
2234 text->TransformTextShapeWithClearanceToPolygon( aOutlines, aLayer, 0, maxError,
2235 ERROR_INSIDE );
2236 break;
2237 }
2238
2239 default:
2240 break;
2241 }
2242 }
2243 }
2244
2245
2246