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