KiCad PCB EDA Suite
Loading...
Searching...
No Matches
multichannel_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24
25#include <board_commit.h>
26#include <tools/pcb_actions.h>
27
30
31#include "multichannel_tool.h"
32
33#include <pcbexpr_evaluator.h>
34
35#include <zone.h>
38#include <pcb_group.h>
39#include <footprint.h>
43#include <optional>
44#include <algorithm>
46#include <pcb_track.h>
47#include <tool/tool_manager.h>
49#include <random>
50#include <core/profile.h>
51#include <wx/log.h>
52#include <wx/richmsgdlg.h>
53#include <pgm_base.h>
54
55
56#define MULTICHANNEL_EXTRA_DEBUG
57
58static const wxString traceMultichannelTool = wxT( "MULTICHANNEL_TOOL" );
59
60
61static void ShowTopologyMismatchReasons( wxWindow* aParent, const wxString& aSummary,
62 const std::vector<wxString>& aReasons )
63{
64 if( !aParent || aReasons.empty() )
65 return;
66
67 wxString reasonText;
68
69 for( size_t idx = 0; idx < aReasons.size(); ++idx )
70 {
71 if( idx > 0 )
72 reasonText += wxT( "\n" );
73
74 reasonText += aReasons[idx];
75 }
76
77 wxRichMessageDialog dlg( aParent, aSummary, _( "Topology mismatch" ), wxICON_ERROR | wxOK );
78 dlg.ShowDetailedText( reasonText );
79 dlg.ShowModal();
80}
81
82
84{
85}
86
87
92
93void MULTICHANNEL_TOOL::ShowMismatchDetails( wxWindow* aParent, const wxString& aSummary,
94 const std::vector<wxString>& aReasons ) const
95{
96 wxWindow* parent = aParent ? aParent : frame();
97 ShowTopologyMismatchReasons( parent, aSummary, aReasons );
98}
99
100
106
107
109 std::set<FOOTPRINT*>& aComponents )
110{
111 if( !aRuleArea || !aRuleArea->m_zone )
112 return false;
113
114 // When we're copying the layout of a design block, we are provided an exact list of items
115 // rather than querying the board for items that are inside the area.
117 {
118 // Get all board connected items that are from the design bloc
119 for( EDA_ITEM* item : aRuleArea->m_designBlockItems )
120 {
121 if( item->Type() == PCB_FOOTPRINT_T )
122 aComponents.insert( static_cast<FOOTPRINT*>( item ) );
123 }
124
125 return (int) aComponents.size();
126 }
127
128
130 PCBEXPR_UCODE ucode;
131 PCBEXPR_CONTEXT ctx, preflightCtx;
132
133 auto reportError =
134 [&]( const wxString& aMessage, int aOffset )
135 {
136 wxLogTrace( traceMultichannelTool, wxT( "ERROR: %s"), aMessage );
137 };
138
139 ctx.SetErrorCallback( reportError );
140 preflightCtx.SetErrorCallback( reportError );
141 compiler.SetErrorCallback( reportError );
142 //compiler.SetDebugReporter( m_reporter );
143
144 wxLogTrace( traceMultichannelTool, wxT( "rule area '%s'" ), aRuleArea->m_zone->GetZoneName() );
145
146 wxString ruleText;
147
148 switch( aRuleArea->m_zone->GetPlacementAreaSourceType() )
149 {
151 ruleText = wxT( "A.memberOfSheetOrChildren('" ) + aRuleArea->m_zone->GetPlacementAreaSource() + wxT( "')" );
152 break;
154 ruleText = wxT( "A.hasComponentClass('" ) + aRuleArea->m_zone->GetPlacementAreaSource() + wxT( "')" );
155 break;
157 ruleText = wxT( "A.memberOfGroup('" ) + aRuleArea->m_zone->GetPlacementAreaSource() + wxT( "')" );
158 break;
160 // For design blocks, handled above outside the rules system
161 break;
162 }
163
164 auto ok = compiler.Compile( ruleText, &ucode, &preflightCtx );
165
166 if( !ok )
167 return false;
168
169 for( FOOTPRINT* fp : board()->Footprints() )
170 {
171 ctx.SetItems( fp, fp );
172 LIBEVAL::VALUE* val = ucode.Run( &ctx );
173
174 if( val->AsDouble() != 0.0 )
175 {
176 wxLogTrace( traceMultichannelTool, wxT( " - %s [sheet %s]" ),
177 fp->GetReference(),
178 fp->GetSheetname() );
179
180 aComponents.insert( fp );
181 }
182 }
183
184 return true;
185}
186
187
188bool MULTICHANNEL_TOOL::findOtherItemsInRuleArea( RULE_AREA* aRuleArea, std::set<BOARD_ITEM*>& aItems )
189{
190 if( !aRuleArea || !aRuleArea->m_zone )
191 return false;
192
193 // When we're copying the layout of a design block, we are provided an exact list of items
194 // rather than querying the board for items that are inside the area.
196 {
197 // Get all board items that aren't footprints or connected items,
198 // since they'll be handled in the the other findXInRuleArea routines
199 for( EDA_ITEM* item : aRuleArea->m_designBlockItems )
200 {
201 if( item->Type() == PCB_FOOTPRINT_T )
202 continue;
203
204 if( dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
205 continue;
206
207 if( item->IsBOARD_ITEM() )
208 aItems.insert( static_cast<BOARD_ITEM*>( item ) );
209 }
210
211 return (int) aItems.size();
212 }
213
214 std::vector<BOARD_ITEM*> result;
215
217 PCBEXPR_UCODE ucode;
218 PCBEXPR_CONTEXT ctx, preflightCtx;
219
220 auto reportError =
221 [&]( const wxString& aMessage, int aOffset )
222 {
223 wxLogTrace( traceMultichannelTool, wxT( "ERROR: %s"), aMessage );
224 };
225
226 ctx.SetErrorCallback( reportError );
227 preflightCtx.SetErrorCallback( reportError );
228 compiler.SetErrorCallback( reportError );
229
230 bool restoreBlankName = false;
231
232 if( aRuleArea->m_zone->GetZoneName().IsEmpty() )
233 {
234 restoreBlankName = true;
235 aRuleArea->m_zone->SetZoneName( aRuleArea->m_zone->m_Uuid.AsString() );
236 }
237
238 wxString ruleText = wxString::Format( wxT( "A.enclosedByArea('%s')" ), aRuleArea->m_zone->GetZoneName() );
239
240 if( !compiler.Compile( ruleText, &ucode, &preflightCtx ) )
241 {
242 if( restoreBlankName )
243 aRuleArea->m_zone->SetZoneName( wxEmptyString );
244
245 return false;
246 }
247
248 auto testAndAdd =
249 [&]( BOARD_ITEM* aItem )
250 {
251 ctx.SetItems( aItem, aItem );
252 auto val = ucode.Run( &ctx );
253
254 if( val->AsDouble() != 0.0 )
255 aItems.insert( aItem );
256 };
257
258 for( ZONE* zone : board()->Zones() )
259 {
260 if( zone == aRuleArea->m_zone )
261 continue;
262
263 testAndAdd( zone );
264 }
265
266 for( BOARD_ITEM* drawing : board()->Drawings() )
267 testAndAdd( drawing );
268
269 for( PCB_GROUP* group : board()->Groups() )
270 {
271 // A group is cloned in its entirety if *all* children are contained
272 bool addGroup = true;
273
274 group->RunOnChildren(
275 [&]( BOARD_ITEM* aItem )
276 {
277 if( aItem->IsType( { PCB_ZONE_T, PCB_SHAPE_T, PCB_BARCODE_T, PCB_DIMENSION_T } ) )
278 {
279 ctx.SetItems( aItem, aItem );
280 LIBEVAL::VALUE* val = ucode.Run( &ctx );
281
282 if( val->AsDouble() == 0.0 )
283 addGroup = false;
284 }
285 },
287
288 if( addGroup )
289 aItems.insert( group );
290 }
291
292 if( restoreBlankName )
293 aRuleArea->m_zone->SetZoneName( wxEmptyString );
294
295 return true;
296}
297
298
299std::set<FOOTPRINT*> MULTICHANNEL_TOOL::queryComponentsInSheet( wxString aSheetName ) const
300{
301 std::set<FOOTPRINT*> rv;
302
303 if( aSheetName.EndsWith( wxT( "/" ) ) )
304 aSheetName.RemoveLast();
305
306 for( FOOTPRINT* fp : board()->Footprints() )
307 {
308 auto sn = fp->GetSheetname();
309 if( sn.EndsWith( wxT( "/" ) ) )
310 sn.RemoveLast();
311
312 if( sn == aSheetName )
313 rv.insert( fp );
314 }
315
316 return rv;
317}
318
319
320std::set<FOOTPRINT*>
321MULTICHANNEL_TOOL::queryComponentsInComponentClass( const wxString& aComponentClassName ) const
322{
323 std::set<FOOTPRINT*> rv;
324
325 for( FOOTPRINT* fp : board()->Footprints() )
326 {
327 if( fp->GetComponentClass()->ContainsClassName( aComponentClassName ) )
328 rv.insert( fp );
329 }
330
331 return rv;
332}
333
334
335std::set<FOOTPRINT*> MULTICHANNEL_TOOL::queryComponentsInGroup( const wxString& aGroupName ) const
336{
337 std::set<FOOTPRINT*> rv;
338
339 for( PCB_GROUP* group : board()->Groups() )
340 {
341 if( group->GetName() == aGroupName )
342 {
343 for( EDA_ITEM* item : group->GetItems() )
344 {
345 if( item->Type() == PCB_FOOTPRINT_T )
346 rv.insert( static_cast<FOOTPRINT*>( item ) );
347 }
348 }
349 }
350
351 return rv;
352}
353
354
355const SHAPE_LINE_CHAIN MULTICHANNEL_TOOL::buildRAOutline( std::set<FOOTPRINT*>& aFootprints,
356 int aMargin )
357{
358 std::vector<VECTOR2I> bbCorners;
359 bbCorners.reserve( aFootprints.size() * 4 );
360
361 for( FOOTPRINT* fp : aFootprints )
362 {
363 const BOX2I bb = fp->GetBoundingBox( false ).GetInflated( aMargin );
364 KIGEOM::CollectBoxCorners( bb, bbCorners );
365 }
366
367 std::vector<VECTOR2I> hullVertices;
368 BuildConvexHull( hullVertices, bbCorners );
369
370 SHAPE_LINE_CHAIN hull( hullVertices );
371
372 // Make the newly computed convex hull use only 90 degree segments
373 return KIGEOM::RectifyPolygon( hull );
374}
375
376
378{
379 using PathAndName = std::pair<wxString, wxString>;
380 std::set<PathAndName> uniqueSheets;
381 std::set<wxString> uniqueComponentClasses;
382 std::set<wxString> uniqueGroups;
383
384 m_areas.m_areas.clear();
385
386 for( const FOOTPRINT* fp : board()->Footprints() )
387 {
388 uniqueSheets.insert( PathAndName( fp->GetSheetname(), fp->GetSheetfile() ) );
389
390 const COMPONENT_CLASS* compClass = fp->GetComponentClass();
391
392 for( const COMPONENT_CLASS* singleClass : compClass->GetConstituentClasses() )
393 uniqueComponentClasses.insert( singleClass->GetName() );
394
395 if( fp->GetParentGroup() && !fp->GetParentGroup()->GetName().IsEmpty() )
396 uniqueGroups.insert( fp->GetParentGroup()->GetName() );
397 }
398
399 for( const PathAndName& sheet : uniqueSheets )
400 {
401 RULE_AREA ent;
402
404 ent.m_generateEnabled = false;
405 ent.m_sheetPath = sheet.first;
406 ent.m_sheetName = sheet.second;
408 m_areas.m_areas.push_back( ent );
409
410 wxLogTrace( traceMultichannelTool, wxT("found sheet '%s' @ '%s' s %d\n"),
411 ent.m_sheetName,
412 ent.m_sheetPath,
413 (int) m_areas.m_areas.size() );
414 }
415
416 for( const wxString& compClass : uniqueComponentClasses )
417 {
418 RULE_AREA ent;
419
421 ent.m_generateEnabled = false;
422 ent.m_componentClass = compClass;
424 m_areas.m_areas.push_back( ent );
425
426 wxLogTrace( traceMultichannelTool, wxT( "found component class '%s' s %d\n" ),
428 static_cast<int>( m_areas.m_areas.size() ) );
429 }
430
431 for( const wxString& groupName : uniqueGroups )
432 {
433 RULE_AREA ent;
434
436 ent.m_generateEnabled = false;
437 ent.m_groupName = groupName;
439 m_areas.m_areas.push_back( ent );
440
441 wxLogTrace( traceMultichannelTool, wxT( "found group '%s' s %d\n" ),
443 static_cast<int>( m_areas.m_areas.size() ) );
444 }
445}
446
447
449{
450 m_areas.m_areas.clear();
451
452 for( ZONE* zone : board()->Zones() )
453 {
454 if( !zone->GetIsRuleArea() )
455 continue;
456
457 if( !zone->GetPlacementAreaEnabled() )
458 continue;
459
460 RULE_AREA area;
461
462 area.m_existsAlready = true;
463 area.m_zone = zone;
464 area.m_ruleName = zone->GetZoneName();
465 area.m_center = zone->Outline()->COutline( 0 ).Centre();
466
468
469 m_areas.m_areas.push_back( area );
470
471 wxLogTrace( traceMultichannelTool, wxT( "RA '%s', %d footprints\n" ), area.m_ruleName,
472 (int) area.m_components.size() );
473 }
474
475 wxLogTrace( traceMultichannelTool, wxT( "Total RAs found: %d\n" ), (int) m_areas.m_areas.size() );
476}
477
478
480{
481 for( RULE_AREA& ra : m_areas.m_areas )
482 {
483 if( ra.m_ruleName == aName )
484 return &ra;
485 }
486
487 return nullptr;
488}
489
490
492{
494}
495
496
498{
499 std::vector<ZONE*> refRAs;
500
501 auto isSelectedItemAnRA =
502 []( EDA_ITEM* aItem ) -> ZONE*
503 {
504 if( !aItem || aItem->Type() != PCB_ZONE_T )
505 return nullptr;
506
507 ZONE* zone = static_cast<ZONE*>( aItem );
508
509 if( !zone->GetIsRuleArea() )
510 return nullptr;
511
512 if( !zone->GetPlacementAreaEnabled() )
513 return nullptr;
514
515 return zone;
516 };
517
518 for( EDA_ITEM* item : selection() )
519 {
520 if( auto zone = isSelectedItemAnRA( item ) )
521 {
522 refRAs.push_back(zone);
523 }
524 else if ( item->Type() == PCB_GROUP_T )
525 {
526 PCB_GROUP *group = static_cast<PCB_GROUP*>( item );
527
528 for( EDA_ITEM* grpItem : group->GetItems() )
529 {
530 if( auto grpZone = isSelectedItemAnRA( grpItem ) )
531 refRAs.push_back( grpZone );
532 }
533 }
534 }
535
536 if( refRAs.size() != 1 )
537 {
540 this,
541 _( "Select a reference Rule Area to copy from..." ),
542 [&]( EDA_ITEM* aItem )
543 {
544 return isSelectedItemAnRA( aItem ) != nullptr;
545 }
546 } );
547
548 return 0;
549 }
550
552
553 int status = CheckRACompatibility( refRAs.front() );
554
555 if( status < 0 )
556 return status;
557
558 if( m_areas.m_areas.size() <= 1 )
559 {
560 frame()->ShowInfoBarError( _( "No Rule Areas to repeat layout to have been found." ), true );
561 return 0;
562 }
563
565 int ret = dialog.ShowModal();
566
567 if( ret != wxID_OK )
568 return 0;
569
570 return RepeatLayout( aEvent, refRAs.front() );
571}
572
573
575{
576 m_areas.m_refRA = nullptr;
577
578 for( RULE_AREA& ra : m_areas.m_areas )
579 {
580 if( ra.m_zone == aRefZone )
581 {
582 m_areas.m_refRA = &ra;
583 break;
584 }
585 }
586
587 if( !m_areas.m_refRA )
588 return -1;
589
590 m_areas.m_compatMap.clear();
591
592 for( RULE_AREA& ra : m_areas.m_areas )
593 {
594 if( ra.m_zone == m_areas.m_refRA->m_zone )
595 continue;
596
597 m_areas.m_compatMap[&ra] = RULE_AREA_COMPAT_DATA();
598
599 resolveConnectionTopology( m_areas.m_refRA, &ra, m_areas.m_compatMap[&ra] );
600 }
601
602 return 0;
603}
604
605
606int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, RULE_AREA& aRefArea, RULE_AREA& aTargetArea )
607{
609
610 if( !resolveConnectionTopology( &aRefArea, &aTargetArea, compat ) )
611 {
612 if( Pgm().IsGUI() )
613 {
614 wxString summary = wxString::Format( _( "Rule Area topologies do not match: %s" ), compat.m_errorMsg );
616 }
617 return -1;
618 }
619
620 BOARD_COMMIT commit( GetManager(), true, false );
621
622 if( !copyRuleAreaContents( &aRefArea, &aTargetArea, &commit, m_areas.m_options, compat ) )
623 {
624 auto errMsg = wxString::Format( _( "Copy Rule Area contents failed between rule areas '%s' and '%s'." ),
625 m_areas.m_refRA->m_zone->GetZoneName(), aTargetArea.m_zone->GetZoneName() );
626
627 commit.Revert();
628
629 if( Pgm().IsGUI() )
630 {
631 frame()->ShowInfoBarError( errMsg, true );
632 }
633
634 return -1;
635 }
636
638 {
639 if( aTargetArea.m_components.size() == 0 || !( *aTargetArea.m_components.begin() )->GetParentGroup() )
640 {
641 commit.Revert();
642
643 if( Pgm().IsGUI() )
644 {
645 frame()->ShowInfoBarError( _( "Target group does not have a group." ), true );
646 }
647
648 return -1;
649 }
650
651 EDA_GROUP* group = ( *aTargetArea.m_components.begin() )->GetParentGroup();
652
653 commit.Add( group->AsEdaItem() );
654
655 for( BOARD_ITEM* item : compat.m_groupableItems )
656 {
657 commit.Modify( item );
658 group->AddItem( item );
659 }
660 }
661
662 commit.Push( _( "Repeat layout" ) );
663
664 return 0;
665}
666
667
668int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
669{
670 int totalCopied = 0;
671
672 BOARD_COMMIT commit( GetManager(), true, false );
673 for( auto& targetArea : m_areas.m_compatMap )
674 {
675 if( !targetArea.second.m_doCopy )
676 {
677 wxLogTrace( traceMultichannelTool, wxT( "skipping copy to RA '%s' (disabled in dialog)\n" ),
678 targetArea.first->m_ruleName );
679 continue;
680 }
681
682 if( !targetArea.second.m_isOk )
683 continue;
684
685 if( !copyRuleAreaContents( m_areas.m_refRA, targetArea.first, &commit, m_areas.m_options, targetArea.second ) )
686 {
687 auto errMsg = wxString::Format(
688 _( "Copy Rule Area contents failed between rule areas '%s' and '%s'." ),
689 m_areas.m_refRA->m_zone->GetZoneName(),
690 targetArea.first->m_zone->GetZoneName() );
691
692 commit.Revert();
693
694 if( Pgm().IsGUI() )
695 frame()->ShowInfoBarError( errMsg, true );
696
697 return -1;
698 }
699 totalCopied++;
700 wxSafeYield();
701 }
702
703 if( m_areas.m_options.m_groupItems )
704 {
705 for( const auto& [targetArea, compatData] : m_areas.m_compatMap )
706 {
707 pruneExistingGroups( commit, compatData.m_affectedItems );
708
709 PCB_GROUP* group = new PCB_GROUP( board() );
710
711 commit.Add( group );
712
713 for( BOARD_ITEM* item : compatData.m_groupableItems )
714 {
715 commit.Modify( item );
716 group->AddItem( item );
717 }
718 }
719 }
720
721 commit.Push( _( "Repeat layout" ) );
722
723 if( Pgm().IsGUI() )
724 frame()->ShowInfoBarMsg( wxString::Format( _( "Copied to %d Rule Areas." ), totalCopied ), true );
725
726 return 0;
727}
728
729
730wxString MULTICHANNEL_TOOL::stripComponentIndex( const wxString& aRef ) const
731{
732 wxString rv;
733
734 // fixme: i'm pretty sure this can be written in a simpler way, but I really suck at figuring
735 // out which wx's built in functions would do it for me. And I hate regexps :-)
736 for( auto k : aRef )
737 {
738 if( !k.IsAscii() )
739 break;
740
741 char c;
742 k.GetAsChar( &c );
743
744 if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c == '_' ) )
745 rv.Append( k );
746 else
747 break;
748 }
749
750 return rv;
751}
752
753
754int MULTICHANNEL_TOOL::findRoutingInRuleArea( RULE_AREA* aRuleArea, std::set<BOARD_CONNECTED_ITEM*>& aOutput,
755 std::shared_ptr<CONNECTIVITY_DATA> aConnectivity,
756 const SHAPE_POLY_SET& aRAPoly, const REPEAT_LAYOUT_OPTIONS& aOpts ) const
757{
758 if( !aRuleArea || !aRuleArea->m_zone )
759 return 0;
760
761 // The user also will consider tracks and vias that are inside the source area but
762 // not connected to any of the source pads to count as "routing" (e.g. stitching vias)
763
764 int count = 0;
765
766 // When we're copying the layout of a design block, we are provided an exact list of items
767 // rather than querying the board for items that are inside the area.
769 {
770 // Get all board connected items that are from the design block, except pads,
771 // which shouldn't be copied
772 for( EDA_ITEM* item : aRuleArea->m_designBlockItems )
773 {
774 // Include any connected items except pads.
775 if( item->Type() == PCB_PAD_T )
776 continue;
777
778 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
779 aOutput.insert( bci );
780 }
781
782 return (int) aOutput.size();
783 }
784
786 PCBEXPR_UCODE ucode;
787 PCBEXPR_CONTEXT ctx, preflightCtx;
788
789 auto reportError = [&]( const wxString& aMessage, int aOffset )
790 {
791 wxLogTrace( traceMultichannelTool, wxT( "ERROR: %s" ), aMessage );
792 };
793
794 ctx.SetErrorCallback( reportError );
795 preflightCtx.SetErrorCallback( reportError );
796 compiler.SetErrorCallback( reportError );
797
798 bool restoreBlankName = false;
799
800 if( aRuleArea->m_zone->GetZoneName().IsEmpty() )
801 {
802 restoreBlankName = true;
803 aRuleArea->m_zone->SetZoneName( aRuleArea->m_zone->m_Uuid.AsString() );
804 }
805
806 wxString ruleText = wxString::Format( wxT( "A.enclosedByArea('%s')" ), aRuleArea->m_zone->GetZoneName() );
807
808 auto testAndAdd =
809 [&]( BOARD_CONNECTED_ITEM* aItem )
810 {
811 if( aOutput.contains( aItem ) )
812 return;
813
814 ctx.SetItems( aItem, aItem );
815 LIBEVAL::VALUE* val = ucode.Run( &ctx );
816
817 if( val->AsDouble() != 0.0 )
818 {
819 aOutput.insert( aItem );
820 count++;
821 }
822 };
823
824 if( compiler.Compile( ruleText, &ucode, &preflightCtx ) )
825 {
826 for( PCB_TRACK* track : board()->Tracks() )
827 testAndAdd( track );
828 }
829
830 if( restoreBlankName )
831 aRuleArea->m_zone->SetZoneName( wxEmptyString );
832
833 return count;
834}
835
836
838 BOARD_COMMIT* aCommit, REPEAT_LAYOUT_OPTIONS aOpts,
839 RULE_AREA_COMPAT_DATA& aCompatData )
840{
841 // copy RA shapes first
842 SHAPE_LINE_CHAIN refOutline = aRefArea->m_zone->Outline()->COutline( 0 );
843 SHAPE_LINE_CHAIN targetOutline = aTargetArea->m_zone->Outline()->COutline( 0 );
844
845 FOOTPRINT* targetAnchorFp = nullptr;
846 VECTOR2I disp = aTargetArea->m_center - aRefArea->m_center;
847 EDA_ANGLE rot = EDA_ANGLE( 0 );
848
849 if( aOpts.m_anchorFp )
850 {
851 for( const auto& [refFP, targetFP] : aCompatData.m_matchingComponents )
852 {
853 if( refFP->GetReference() == aOpts.m_anchorFp->GetReference() )
854 targetAnchorFp = targetFP;
855 }
856
857 if( targetAnchorFp )
858 {
859 VECTOR2I oldpos = aOpts.m_anchorFp->GetPosition();
860 rot = EDA_ANGLE( targetAnchorFp->GetOrientationDegrees() - aOpts.m_anchorFp->GetOrientationDegrees() );
861 aOpts.m_anchorFp->Rotate( VECTOR2( 0, 0 ), EDA_ANGLE( rot ) );
862 oldpos = aOpts.m_anchorFp->GetPosition();
863 VECTOR2I newpos = targetAnchorFp->GetPosition();
864 disp = newpos - oldpos;
865 aOpts.m_anchorFp->Rotate( VECTOR2( 0, 0 ), EDA_ANGLE( -rot ) );
866 }
867 }
868
869 SHAPE_POLY_SET refPoly;
870 refPoly.AddOutline( refOutline );
871 refPoly.CacheTriangulation( false );
872
873 SHAPE_POLY_SET targetPoly;
874
875 SHAPE_LINE_CHAIN newTargetOutline( refOutline );
876 newTargetOutline.Rotate( rot, VECTOR2( 0, 0 ) );
877 newTargetOutline.Move( disp );
878 targetPoly.AddOutline( newTargetOutline );
879 targetPoly.CacheTriangulation( false );
880
881 auto connectivity = board()->GetConnectivity();
882
883 // Only stage changes for a target Rule Area zone if it actually belongs to the board.
884 // In some workflows (e.g. ApplyDesignBlockLayout), the target area is a temporary zone
885 // and is not added to the BOARD.
886 bool targetZoneOnBoard = false;
887
888 if( aTargetArea->m_zone )
889 {
890 for( ZONE* z : board()->Zones() )
891 {
892 if( z == aTargetArea->m_zone )
893 {
894 targetZoneOnBoard = true;
895 break;
896 }
897 }
898 }
899
900 if( targetZoneOnBoard )
901 {
902 aCommit->Modify( aTargetArea->m_zone );
903 aCompatData.m_affectedItems.insert( aTargetArea->m_zone );
904 aCompatData.m_groupableItems.insert( aTargetArea->m_zone );
905 }
906
907 if( aOpts.m_copyRouting )
908 {
909 std::set<BOARD_CONNECTED_ITEM*> refRouting;
910 std::set<BOARD_CONNECTED_ITEM*> targetRouting;
911
912 wxLogTrace( traceMultichannelTool, wxT( "copying routing: %d fps\n" ),
913 (int) aCompatData.m_matchingComponents.size() );
914
915 std::set<int> refc;
916 std::set<int> targc;
917
918 for( const auto& [refFP, targetFP] : aCompatData.m_matchingComponents )
919 {
920 for( PAD* pad : refFP->Pads() )
921 refc.insert( pad->GetNetCode() );
922
923 for( PAD* pad : targetFP->Pads() )
924 targc.insert( pad->GetNetCode() );
925 }
926
927 findRoutingInRuleArea( aTargetArea, targetRouting, connectivity, targetPoly, aOpts );
928 findRoutingInRuleArea( aRefArea, refRouting, connectivity, refPoly, aOpts );
929
930 for( BOARD_CONNECTED_ITEM* item : targetRouting )
931 {
932 // Never remove pads as part of routing copy.
933 if( item->Type() == PCB_PAD_T )
934 continue;
935
936 if( item->IsLocked() && !aOpts.m_includeLockedItems )
937 continue;
938
939 if( aOpts.m_connectedRoutingOnly && !targc.contains( item->GetNetCode() ) )
940 continue;
941
942 // item already removed
943 if( aCommit->GetStatus( item ) != 0 )
944 continue;
945
946 if( aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
947 {
948 aCompatData.m_affectedItems.insert( item );
949 aCommit->Remove( item );
950 }
951 }
952
953 for( BOARD_CONNECTED_ITEM* item : refRouting )
954 {
955 // Never copy pads as part of routing copy.
956 if( item->Type() == PCB_PAD_T )
957 continue;
958
959 if( item->IsLocked() && !aOpts.m_includeLockedItems )
960 continue;
961
962 if( aOpts.m_connectedRoutingOnly && !refc.contains( item->GetNetCode() ) )
963 continue;
964
965 if( !aRefArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
966 continue;
967
968 if( !aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
969 continue;
970
971 BOARD_CONNECTED_ITEM* copied = static_cast<BOARD_CONNECTED_ITEM*>( item->Duplicate( false ) );
972
973 fixupNet( item, copied, aCompatData.m_matchingComponents );
974
975 copied->Rotate( VECTOR2( 0, 0 ), rot );
976 copied->Move( disp );
977 aCompatData.m_groupableItems.insert( copied );
978 aCommit->Add( copied );
979 }
980 }
981
982 if( aOpts.m_copyOtherItems )
983 {
984 std::set<BOARD_ITEM*> sourceItems;
985 std::set<BOARD_ITEM*> targetItems;
986
987 findOtherItemsInRuleArea( aRefArea, sourceItems );
988 findOtherItemsInRuleArea( aTargetArea, targetItems );
989
990 for( BOARD_ITEM* item : targetItems )
991 {
992 if( item->Type() == PCB_TEXT_T && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
993 continue;
994
995 if( item->IsLocked() && !aOpts.m_includeLockedItems )
996 continue;
997
998 // item already removed
999 if( aCommit->GetStatus( item ) != 0 )
1000 continue;
1001
1002 if( item->Type() != PCB_ZONE_T )
1003 {
1004 if( aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
1005 {
1006 aCompatData.m_affectedItems.insert( item );
1007 aCommit->Remove( item );
1008 }
1009 }
1010 else
1011 {
1012 ZONE* zone = static_cast<ZONE*>( item );
1013
1014 // Check all zone layers are included in the rule area
1015 bool layerMismatch = false;
1016 LSET zoneLayers = zone->GetLayerSet();
1017
1018 for( const PCB_LAYER_ID& layer : zoneLayers )
1019 {
1020 if( !aTargetArea->m_zone->GetLayerSet().Contains( layer ) )
1021 layerMismatch = true;
1022 }
1023
1024 if( !layerMismatch )
1025 {
1026 aCompatData.m_affectedItems.insert( zone );
1027 aCommit->Remove( zone );
1028 }
1029 }
1030 }
1031
1032 for( BOARD_ITEM* item : sourceItems )
1033 {
1034 if( item->Type() == PCB_TEXT_T && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
1035 continue;
1036
1037 if( item->IsLocked() && !aOpts.m_includeLockedItems )
1038 continue;
1039
1040 BOARD_ITEM* copied = nullptr;
1041
1042 if( item->Type() != PCB_ZONE_T )
1043 {
1044 if( !aRefArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
1045 continue;
1046
1047 if( !aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
1048 continue;
1049
1050 if( item->Type() == PCB_GROUP_T )
1051 copied = static_cast<PCB_GROUP*>( item )->DeepClone();
1052 else
1053 copied = static_cast<BOARD_ITEM*>( item->Clone() );
1054 }
1055 else
1056 {
1057 ZONE* zone = static_cast<ZONE*>( item );
1058
1059 // Check all zone layers are included in the rule area
1060 bool layerMismatch = false;
1061 LSET zoneLayers = zone->GetLayerSet();
1062
1063 for( const PCB_LAYER_ID& layer : zoneLayers )
1064 {
1065 if( !aRefArea->m_zone->GetLayerSet().Contains( layer )
1066 || !aTargetArea->m_zone->GetLayerSet().Contains( layer ) )
1067 {
1068 layerMismatch = true;
1069 }
1070 }
1071
1072 if( layerMismatch )
1073 continue;
1074
1075 ZONE* targetZone = static_cast<ZONE*>( item->Duplicate( false ) );
1076 fixupNet( zone, targetZone, aCompatData.m_matchingComponents );
1077
1078 copied = targetZone;
1079 }
1080
1081 if( copied )
1082 {
1083 copied->ClearFlags();
1084 copied->Rotate( VECTOR2( 0, 0 ), rot );
1085 copied->Move( disp );
1086 aCompatData.m_groupableItems.insert( copied );
1087 aCommit->Add( copied );
1088 }
1089 }
1090 }
1091
1092 if( aOpts.m_copyPlacement )
1093 {
1094 for( const auto& [refFP, targetFP] : aCompatData.m_matchingComponents )
1095 {
1096 if( !aRefArea->m_zone->GetLayerSet().Contains( refFP->GetLayer() ) )
1097 {
1098 wxLogTrace( traceMultichannelTool, wxT( "discard ref:%s (ref layer)\n" ),
1099 refFP->GetReference() );
1100 continue;
1101 }
1102 if( !aTargetArea->m_zone->GetLayerSet().Contains( refFP->GetLayer() ) )
1103 {
1104 wxLogTrace( traceMultichannelTool, wxT( "discard ref:%s (target layer)\n" ),
1105 refFP->GetReference() );
1106 continue;
1107 }
1108
1109 // Ignore footprints outside of the rule area
1110 if( !refFP->GetEffectiveShape( refFP->GetLayer() )->Collide( &refPoly, 0 ) )
1111 continue;
1112
1113 if( targetFP->IsLocked() && !aOpts.m_includeLockedItems )
1114 continue;
1115
1116 aCommit->Modify( targetFP );
1117
1118 targetFP->SetLayerAndFlip( refFP->GetLayer() );
1119 targetFP->SetOrientation( refFP->GetOrientation() );
1120 targetFP->SetPosition( refFP->GetPosition() );
1121 targetFP->Rotate( VECTOR2( 0, 0 ), rot );
1122 targetFP->Move( disp );
1123 for( PCB_FIELD* refField : refFP->GetFields() )
1124 {
1125 if( !refField->IsVisible() )
1126 continue;
1127
1128 PCB_FIELD* targetField = targetFP->GetField( refField->GetName() );
1129 wxCHECK2( targetField, continue );
1130
1131 targetField->SetAttributes( refField->GetAttributes() );
1132 targetField->SetPosition( refField->GetPosition() );
1133 targetField->Rotate( VECTOR2( 0, 0 ), rot );
1134 targetField->Move( disp );
1135 targetField->SetIsKnockout( refField->IsKnockout() );
1136 }
1137
1138 aCompatData.m_affectedItems.insert( targetFP );
1139 aCompatData.m_groupableItems.insert( targetFP );
1140 }
1141 }
1142
1143 aTargetArea->m_zone->RemoveAllContours();
1144 aTargetArea->m_zone->AddPolygon( newTargetOutline );
1145 aTargetArea->m_zone->UnHatchBorder();
1146 aTargetArea->m_zone->HatchBorder();
1147
1148 return true;
1149}
1150
1156 TMATCH::COMPONENT_MATCHES& aComponentMatches )
1157{
1158 auto connectivity = board()->GetConnectivity();
1159 const std::vector<BOARD_CONNECTED_ITEM*> refConnectedPads = connectivity->GetNetItems( aRef->GetNetCode(),
1160 { PCB_PAD_T } );
1161
1162 for( const BOARD_CONNECTED_ITEM* refConItem : refConnectedPads )
1163 {
1164 if( refConItem->Type() != PCB_PAD_T )
1165 continue;
1166
1167 const PAD* refPad = static_cast<const PAD*>( refConItem );
1168 FOOTPRINT* sourceFootprint = refPad->GetParentFootprint();
1169
1170 if( aComponentMatches.contains( sourceFootprint ) )
1171 {
1172 const FOOTPRINT* targetFootprint = aComponentMatches[sourceFootprint];
1173 std::vector<const PAD*> targetFpPads = targetFootprint->GetPads( refPad->GetNumber() );
1174
1175 if( !targetFpPads.empty() )
1176 {
1177 int targetNetCode = targetFpPads[0]->GetNet()->GetNetCode();
1178 aTarget->SetNetCode( targetNetCode );
1179
1180 break;
1181 }
1182 }
1183 }
1184}
1185
1186
1188 RULE_AREA_COMPAT_DATA& aMatches )
1189{
1190 using namespace TMATCH;
1191
1192 std::unique_ptr<CONNECTION_GRAPH> cgRef ( CONNECTION_GRAPH::BuildFromFootprintSet( aRefArea->m_components ) );
1193 std::unique_ptr<CONNECTION_GRAPH> cgTarget ( CONNECTION_GRAPH::BuildFromFootprintSet( aTargetArea->m_components ) );
1194
1195 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> mismatchReasons;
1196 bool status = cgRef->FindIsomorphism( cgTarget.get(), aMatches.m_matchingComponents, mismatchReasons );
1197
1198 aMatches.m_isOk = status;
1199
1200 if( status )
1201 {
1202 aMatches.m_errorMsg = _( "OK" );
1203 aMatches.m_mismatchReasons.clear();
1204 return true;
1205 }
1206
1207 aMatches.m_mismatchReasons.clear();
1208
1209 for( const auto& reason : mismatchReasons )
1210 {
1211 if( reason.m_reason.IsEmpty() )
1212 continue;
1213
1214 if( !reason.m_reference.IsEmpty() && !reason.m_candidate.IsEmpty() )
1215 {
1216 aMatches.m_mismatchReasons.push_back( wxString::Format( wxT( "%s -> %s: %s" ),
1217 reason.m_reference,
1218 reason.m_candidate,
1219 reason.m_reason ) );
1220 }
1221 else if( !reason.m_reference.IsEmpty() )
1222 {
1223 aMatches.m_mismatchReasons.push_back( wxString::Format( wxT( "%s: %s" ),
1224 reason.m_reference,
1225 reason.m_reason ) );
1226 }
1227 else
1228 aMatches.m_mismatchReasons.push_back( reason.m_reason );
1229 }
1230
1231 if( aMatches.m_mismatchReasons.empty() )
1232 aMatches.m_mismatchReasons.push_back( _( "Topology mismatch" ) );
1233
1234 aMatches.m_errorMsg = aMatches.m_mismatchReasons.front();
1235
1236 return status;
1237}
1238
1239
1241 const std::unordered_set<BOARD_ITEM*>& aItemsToRemove )
1242{
1243 // Note: groups are only collections, not "real" hierarchy. A group's members are still parented
1244 // by the board (and therefore nested groups are still in the board's list of groups).
1245 for( PCB_GROUP* group : board()->Groups() )
1246 {
1247 std::vector<EDA_ITEM*> pruneList;
1248
1249 for( EDA_ITEM* refItem : group->GetItems() )
1250 {
1251 for( BOARD_ITEM* testItem : aItemsToRemove )
1252 {
1253 if( refItem->m_Uuid == testItem->m_Uuid )
1254 pruneList.push_back( refItem );
1255 }
1256 }
1257
1258 if( !pruneList.empty() )
1259 {
1260 aCommit.Modify( group );
1261
1262 for( EDA_ITEM* item : pruneList )
1263 group->RemoveItem( item );
1264
1265 if( group->GetItems().empty() )
1266 aCommit.Remove( group );
1267 }
1268 }
1269
1270 return false;
1271}
1272
1273
1275{
1276 if( Pgm().IsGUI() )
1277 {
1279
1280 if( m_areas.m_areas.size() <= 1 )
1281 {
1282 frame()->ShowInfoBarError( _( "Cannot auto-generate any placement areas because the "
1283 "schematic has only one or no hierarchical sheets, "
1284 "groups, or component classes." ),
1285 true );
1286 return 0;
1287 }
1288
1290 int ret = dialog.ShowModal();
1291
1292 if( ret != wxID_OK )
1293 return 0;
1294 }
1295
1296 for( ZONE* zone : board()->Zones() )
1297 {
1298 if( !zone->GetIsRuleArea() )
1299 continue;
1300
1301 if( !zone->GetPlacementAreaEnabled() )
1302 continue;
1303
1304 std::set<FOOTPRINT*> components;
1305 RULE_AREA zoneRA;
1306 zoneRA.m_zone = zone;
1307 zoneRA.m_sourceType = zone->GetPlacementAreaSourceType();
1308 findComponentsInRuleArea( &zoneRA, components );
1309
1310 if( components.empty() )
1311 continue;
1312
1313 for( RULE_AREA& ra : m_areas.m_areas )
1314 {
1315 if( components == ra.m_components )
1316 {
1317 if( zone->GetPlacementAreaSourceType() == PLACEMENT_SOURCE_T::SHEETNAME )
1318 {
1319 wxLogTrace( traceMultichannelTool,
1320 wxT( "Placement rule area for sheet '%s' already exists as '%s'\n" ),
1321 ra.m_sheetPath, zone->GetZoneName() );
1322 }
1323 else if( zone->GetPlacementAreaSourceType() == PLACEMENT_SOURCE_T::COMPONENT_CLASS )
1324 {
1325 wxLogTrace( traceMultichannelTool,
1326 wxT( "Placement rule area for component class '%s' already exists as '%s'\n" ),
1327 ra.m_componentClass, zone->GetZoneName() );
1328 }
1329 else
1330 {
1331 wxLogTrace( traceMultichannelTool,
1332 wxT( "Placement rule area for group '%s' already exists as '%s'\n" ),
1333 ra.m_groupName, zone->GetZoneName() );
1334 }
1335
1336 ra.m_oldZone = zone;
1337 ra.m_existsAlready = true;
1338 }
1339 }
1340 }
1341
1342 wxLogTrace( traceMultichannelTool, wxT( "%d placement areas found\n" ), (int) m_areas.m_areas.size() );
1343
1344 BOARD_COMMIT commit( GetManager(), true, false );
1345
1346 for( RULE_AREA& ra : m_areas.m_areas )
1347 {
1348 if( !ra.m_generateEnabled )
1349 continue;
1350
1351 if( ra.m_existsAlready && !m_areas.m_replaceExisting )
1352 continue;
1353
1354 if( ra.m_components.empty() )
1355 continue;
1356
1357 SHAPE_LINE_CHAIN raOutline = buildRAOutline( ra.m_components, 100000 );
1358
1359 std::unique_ptr<ZONE> newZone( new ZONE( board() ) );
1360
1362 newZone->SetZoneName( wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_sheetPath ) );
1364 newZone->SetZoneName( wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_componentClass ) );
1365 else
1366 newZone->SetZoneName( wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_groupName ) );
1367
1368 wxLogTrace( traceMultichannelTool, wxT( "Generated rule area '%s' (%d components)\n" ),
1369 newZone->GetZoneName(),
1370 (int) ra.m_components.size() );
1371
1372 newZone->SetIsRuleArea( true );
1373 newZone->SetLayerSet( LSET::AllCuMask() );
1374 newZone->SetPlacementAreaEnabled( true );
1375 newZone->SetDoNotAllowZoneFills( false );
1376 newZone->SetDoNotAllowVias( false );
1377 newZone->SetDoNotAllowTracks( false );
1378 newZone->SetDoNotAllowPads( false );
1379 newZone->SetDoNotAllowFootprints( false );
1380
1382 {
1383 newZone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::SHEETNAME );
1384 newZone->SetPlacementAreaSource( ra.m_sheetPath );
1385 }
1387 {
1388 newZone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::COMPONENT_CLASS );
1389 newZone->SetPlacementAreaSource( ra.m_componentClass );
1390 }
1391 else
1392 {
1393 newZone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::GROUP_PLACEMENT );
1394 newZone->SetPlacementAreaSource( ra.m_groupName );
1395 }
1396
1397 newZone->AddPolygon( raOutline );
1398 newZone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
1399
1400 if( ra.m_existsAlready )
1401 {
1402 commit.Remove( ra.m_oldZone );
1403 }
1404
1405 ra.m_zone = newZone.release();
1406 commit.Add( ra.m_zone );
1407 }
1408
1409 // fixme: handle corner cases where the items belonging to a Rule Area already
1410 // belong to other groups.
1411
1412 if( m_areas.m_options.m_groupItems )
1413 {
1414 for( RULE_AREA& ra : m_areas.m_areas )
1415 {
1416 if( !ra.m_generateEnabled )
1417 continue;
1418
1419 if( ra.m_existsAlready && !m_areas.m_replaceExisting )
1420 continue;
1421
1422 std::unordered_set<BOARD_ITEM*> toPrune;
1423
1424 std::copy( ra.m_components.begin(), ra.m_components.end(), std::inserter( toPrune, toPrune.begin() ) );
1425
1426 if( ra.m_existsAlready )
1427 toPrune.insert( ra.m_zone );
1428
1429 pruneExistingGroups( commit, toPrune );
1430
1431 PCB_GROUP* group = new PCB_GROUP( board() );
1432
1433 commit.Add( group );
1434
1435 commit.Modify( ra.m_zone );
1436 group->AddItem( ra.m_zone );
1437
1438 for( FOOTPRINT* fp : ra.m_components )
1439 {
1440 commit.Modify( fp );
1441 group->AddItem( fp );
1442 }
1443 }
1444 }
1445
1446 commit.Push( _( "Auto-generate placement rule areas" ) );
1447
1448 return true;
1449}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
virtual void Revert() override
Revert the commit by restoring the modified items state.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:320
FOOTPRINT * GetParentFootprint() const
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:538
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:72
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
Definition commit.cpp:167
A lightweight representation of a component class.
const std::vector< COMPONENT_CLASS * > & GetConstituentClasses() const
Fetches a vector of the constituent classes for this (effective) class.
const std::vector< BOARD_CONNECTED_ITEM * > GetNetItems(int aNetCode, const std::vector< KICAD_T > &aTypes) const
Function GetNetItems() Returns the list of items that belong to a certain net.
int ShowModal() override
A set of EDA_ITEMs (i.e., without duplicates).
Definition eda_group.h:46
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
const KIID m_Uuid
Definition eda_item.h:516
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual bool IsType(const std::vector< KICAD_T > &aScanTypes) const
Check whether the item is one of the listed types.
Definition eda_item.h:192
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition eda_text.cpp:444
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
double GetOrientationDegrees() const
Definition footprint.h:264
std::vector< const PAD * > GetPads(const wxString &aPadNumber, const PAD *aIgnore=nullptr) const
const wxString & GetReference() const
Definition footprint.h:661
VECTOR2I GetPosition() const override
Definition footprint.h:245
bool IsBOARD_ITEM() const
Definition view_item.h:102
wxString AsString() const
Definition kiid.cpp:246
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
VALUE * Run(CONTEXT *ctx)
virtual double AsDouble() const
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:582
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
int CheckRACompatibility(ZONE *aRefZone)
std::set< FOOTPRINT * > queryComponentsInSheet(wxString aSheetName) const
bool findOtherItemsInRuleArea(RULE_AREA *aRuleArea, std::set< BOARD_ITEM * > &aItems)
int repeatLayout(const TOOL_EVENT &aEvent)
bool findComponentsInRuleArea(RULE_AREA *aRuleArea, std::set< FOOTPRINT * > &aComponents)
void UpdatePickedItem(const EDA_ITEM *aItem) override
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
const SHAPE_LINE_CHAIN buildRAOutline(std::set< FOOTPRINT * > &aFootprints, int aMargin)
int findRoutingInRuleArea(RULE_AREA *aRuleArea, std::set< BOARD_CONNECTED_ITEM * > &aOutput, std::shared_ptr< CONNECTIVITY_DATA > aConnectivity, const SHAPE_POLY_SET &aRAPoly, const REPEAT_LAYOUT_OPTIONS &aOpts) const
wxString stripComponentIndex(const wxString &aRef) const
bool resolveConnectionTopology(RULE_AREA *aRefArea, RULE_AREA *aTargetArea, RULE_AREA_COMPAT_DATA &aMatches)
RULE_AREAS_DATA m_areas
bool pruneExistingGroups(COMMIT &aCommit, const std::unordered_set< BOARD_ITEM * > &aItemsToCheck)
void ShowMismatchDetails(wxWindow *aParent, const wxString &aSummary, const std::vector< wxString > &aReasons) const
int RepeatLayout(const TOOL_EVENT &aEvent, ZONE *aRefZone)
int AutogenerateRuleAreas(const TOOL_EVENT &aEvent)
void fixupNet(BOARD_CONNECTED_ITEM *aRef, BOARD_CONNECTED_ITEM *aTarget, TMATCH::COMPONENT_MATCHES &aComponentMatches)
Attempts to make sure copied items are assigned the right net.
bool copyRuleAreaContents(RULE_AREA *aRefArea, RULE_AREA *aTargetArea, BOARD_COMMIT *aCommit, REPEAT_LAYOUT_OPTIONS aOpts, RULE_AREA_COMPAT_DATA &aCompatData)
std::set< FOOTPRINT * > queryComponentsInComponentClass(const wxString &aComponentClassName) const
RULE_AREA * findRAByName(const wxString &aName)
std::set< FOOTPRINT * > queryComponentsInGroup(const wxString &aGroupName) const
Definition pad.h:54
const wxString & GetNumber() const
Definition pad.h:136
void SetItems(BOARD_ITEM *a, BOARD_ITEM *b=nullptr)
static TOOL_ACTION repeatLayout
static TOOL_ACTION generatePlacementRuleAreas
static TOOL_ACTION selectItemInteractively
Selection of reference points/items.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
virtual void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:89
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition pcb_text.h:94
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition pcb_text.cpp:404
T * frame() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
const PCB_SELECTION & selection() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
virtual void CacheTriangulation(bool aPartition=true, bool aSimplify=false)
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
static std::unique_ptr< CONNECTION_GRAPH > BuildFromFootprintSet(const std::set< FOOTPRINT * > &aFps)
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition tool_base.h:146
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
Generic, UI-independent tool event.
Definition tool_event.h:171
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
Define a general 2D-vector/point.
Definition vector2d.h:71
Handle a list of polygons defining a copper zone.
Definition zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:701
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition zone.cpp:1106
wxString GetPlacementAreaSource() const
Definition zone.h:706
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition zone.cpp:1233
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
Definition zone.h:708
SHAPE_POLY_SET * Outline()
Definition zone.h:331
const wxString & GetZoneName() const
Definition zone.h:159
bool GetPlacementAreaEnabled() const
Definition zone.h:703
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:136
void SetZoneName(const wxString &aName)
Definition zone.h:160
void UnHatchBorder()
Clear the zone's hatch.
Definition zone.cpp:1227
void RemoveAllContours(void)
Definition zone.h:550
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
#define _(s)
@ RECURSE
Definition eda_item.h:51
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
static const wxString traceMultichannelTool
static void ShowTopologyMismatchReasons(wxWindow *aParent, const wxString &aSummary, const std::vector< wxString > &aReasons)
SHAPE_LINE_CHAIN RectifyPolygon(const SHAPE_LINE_CHAIN &aPoly)
void CollectBoxCorners(const BOX2I &aBox, std::vector< VECTOR2I > &aCorners)
Add the 4 corners of a BOX2I to a vector.
std::map< FOOTPRINT *, FOOTPRINT * > COMPONENT_MATCHES
Definition topo_match.h:156
Class to handle a set of BOARD_ITEMs.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:946
see class PGM_BASE
Utility functions for working with shapes.
std::vector< wxString > m_mismatchReasons
std::unordered_set< BOARD_ITEM * > m_affectedItems
Filled in by copyRuleAreaContents with items that were affected by the copy operation.
TMATCH::COMPONENT_MATCHES m_matchingComponents
std::unordered_set< BOARD_ITEM * > m_groupableItems
Filled in by copyRuleAreaContents with affected items that can be grouped together.
VECTOR2I m_center
std::unordered_set< EDA_ITEM * > m_designBlockItems
wxString m_sheetName
PLACEMENT_SOURCE_T m_sourceType
wxString m_componentClass
std::set< FOOTPRINT * > m_components
wxString m_ruleName
wxString m_groupName
wxString m_sheetPath
bool copied
wxString result
Test unit parsing edge cases and error handling.
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:108
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:86
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695