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( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
205 {
206 if( !boardItem->IsConnected() )
207 aItems.insert( boardItem );
208 }
209 }
210
211 return aItems.size() > 0;
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 {
268 if( !drawing->IsConnected() )
269 testAndAdd( drawing );
270 }
271
272 if( restoreBlankName )
273 aRuleArea->m_zone->SetZoneName( wxEmptyString );
274
275 return true;
276}
277
278
279std::set<FOOTPRINT*> MULTICHANNEL_TOOL::queryComponentsInSheet( wxString aSheetName ) const
280{
281 std::set<FOOTPRINT*> rv;
282
283 if( aSheetName.EndsWith( wxT( "/" ) ) )
284 aSheetName.RemoveLast();
285
286 for( FOOTPRINT* fp : board()->Footprints() )
287 {
288 auto sn = fp->GetSheetname();
289 if( sn.EndsWith( wxT( "/" ) ) )
290 sn.RemoveLast();
291
292 if( sn == aSheetName )
293 rv.insert( fp );
294 }
295
296 return rv;
297}
298
299
300std::set<FOOTPRINT*>
301MULTICHANNEL_TOOL::queryComponentsInComponentClass( const wxString& aComponentClassName ) const
302{
303 std::set<FOOTPRINT*> rv;
304
305 for( FOOTPRINT* fp : board()->Footprints() )
306 {
307 if( fp->GetComponentClass()->ContainsClassName( aComponentClassName ) )
308 rv.insert( fp );
309 }
310
311 return rv;
312}
313
314
315std::set<FOOTPRINT*> MULTICHANNEL_TOOL::queryComponentsInGroup( const wxString& aGroupName ) const
316{
317 std::set<FOOTPRINT*> rv;
318
319 for( PCB_GROUP* group : board()->Groups() )
320 {
321 if( group->GetName() == aGroupName )
322 {
323 for( EDA_ITEM* item : group->GetItems() )
324 {
325 if( item->Type() == PCB_FOOTPRINT_T )
326 rv.insert( static_cast<FOOTPRINT*>( item ) );
327 }
328 }
329 }
330
331 return rv;
332}
333
334
335const SHAPE_LINE_CHAIN MULTICHANNEL_TOOL::buildRAOutline( std::set<FOOTPRINT*>& aFootprints, int aMargin )
336{
337 std::vector<VECTOR2I> bbCorners;
338 bbCorners.reserve( aFootprints.size() * 4 );
339
340 for( FOOTPRINT* fp : aFootprints )
341 {
342 const BOX2I bb = fp->GetBoundingBox( false ).GetInflated( aMargin );
343 KIGEOM::CollectBoxCorners( bb, bbCorners );
344 }
345
346 std::vector<VECTOR2I> hullVertices;
347 BuildConvexHull( hullVertices, bbCorners );
348
349 SHAPE_LINE_CHAIN hull( hullVertices );
350
351 // Make the newly computed convex hull use only 90 degree segments
352 return KIGEOM::RectifyPolygon( hull );
353}
354
355
357{
358 using PathAndName = std::pair<wxString, wxString>;
359 std::set<PathAndName> uniqueSheets;
360 std::set<wxString> uniqueComponentClasses;
361 std::set<wxString> uniqueGroups;
362
363 m_areas.m_areas.clear();
364
365 for( const FOOTPRINT* fp : board()->Footprints() )
366 {
367 uniqueSheets.insert( PathAndName( fp->GetSheetname(), fp->GetSheetfile() ) );
368
369 const COMPONENT_CLASS* compClass = fp->GetComponentClass();
370
371 for( const COMPONENT_CLASS* singleClass : compClass->GetConstituentClasses() )
372 uniqueComponentClasses.insert( singleClass->GetName() );
373
374 if( fp->GetParentGroup() && !fp->GetParentGroup()->GetName().IsEmpty() )
375 uniqueGroups.insert( fp->GetParentGroup()->GetName() );
376 }
377
378 for( const PathAndName& sheet : uniqueSheets )
379 {
380 RULE_AREA ent;
381
383 ent.m_generateEnabled = false;
384 ent.m_sheetPath = sheet.first;
385 ent.m_sheetName = sheet.second;
387 m_areas.m_areas.push_back( ent );
388
389 wxLogTrace( traceMultichannelTool, wxT("found sheet '%s' @ '%s' s %d\n"),
390 ent.m_sheetName,
391 ent.m_sheetPath,
392 (int) m_areas.m_areas.size() );
393 }
394
395 for( const wxString& compClass : uniqueComponentClasses )
396 {
397 RULE_AREA ent;
398
400 ent.m_generateEnabled = false;
401 ent.m_componentClass = compClass;
403 m_areas.m_areas.push_back( ent );
404
405 wxLogTrace( traceMultichannelTool, wxT( "found component class '%s' s %d\n" ),
407 static_cast<int>( m_areas.m_areas.size() ) );
408 }
409
410 for( const wxString& groupName : uniqueGroups )
411 {
412 RULE_AREA ent;
413
415 ent.m_generateEnabled = false;
416 ent.m_groupName = groupName;
418 m_areas.m_areas.push_back( ent );
419
420 wxLogTrace( traceMultichannelTool, wxT( "found group '%s' s %d\n" ),
422 static_cast<int>( m_areas.m_areas.size() ) );
423 }
424}
425
426
428{
429 m_areas.m_areas.clear();
430
431 for( ZONE* zone : board()->Zones() )
432 {
433 if( !zone->GetIsRuleArea() )
434 continue;
435
436 if( !zone->GetPlacementAreaEnabled() )
437 continue;
438
439 RULE_AREA area;
440
441 area.m_existsAlready = true;
442 area.m_zone = zone;
443 area.m_ruleName = zone->GetZoneName();
444 area.m_center = zone->Outline()->COutline( 0 ).Centre();
445
447
448 m_areas.m_areas.push_back( area );
449
450 wxLogTrace( traceMultichannelTool, wxT( "RA '%s', %d footprints\n" ), area.m_ruleName,
451 (int) area.m_components.size() );
452 }
453
454 wxLogTrace( traceMultichannelTool, wxT( "Total RAs found: %d\n" ), (int) m_areas.m_areas.size() );
455}
456
457
459{
460 for( RULE_AREA& ra : m_areas.m_areas )
461 {
462 if( ra.m_ruleName == aName )
463 return &ra;
464 }
465
466 return nullptr;
467}
468
469
471{
473}
474
475
477{
478 std::vector<ZONE*> refRAs;
479
480 auto isSelectedItemAnRA =
481 []( EDA_ITEM* aItem ) -> ZONE*
482 {
483 if( !aItem || aItem->Type() != PCB_ZONE_T )
484 return nullptr;
485
486 ZONE* zone = static_cast<ZONE*>( aItem );
487
488 if( !zone->GetIsRuleArea() )
489 return nullptr;
490
491 if( !zone->GetPlacementAreaEnabled() )
492 return nullptr;
493
494 return zone;
495 };
496
497 for( EDA_ITEM* item : selection() )
498 {
499 if( ZONE* zone = isSelectedItemAnRA( item ) )
500 {
501 refRAs.push_back( zone );
502 }
503 else if( item->Type() == PCB_GROUP_T )
504 {
505 PCB_GROUP *group = static_cast<PCB_GROUP*>( item );
506
507 for( EDA_ITEM* grpItem : group->GetItems() )
508 {
509 if( ZONE* grpZone = isSelectedItemAnRA( grpItem ) )
510 refRAs.push_back( grpZone );
511 }
512 }
513 }
514
515 if( refRAs.size() != 1 )
516 {
519 this,
520 _( "Select a reference Rule Area to copy from..." ),
521 [&]( EDA_ITEM* aItem )
522 {
523 return isSelectedItemAnRA( aItem ) != nullptr;
524 }
525 } );
526
527 return 0;
528 }
529
531
532 int status = CheckRACompatibility( refRAs.front() );
533
534 if( status < 0 )
535 return status;
536
537 if( m_areas.m_areas.size() <= 1 )
538 {
539 frame()->ShowInfoBarError( _( "No Rule Areas to repeat layout to have been found." ), true );
540 return 0;
541 }
542
544 int ret = dialog.ShowModal();
545
546 if( ret != wxID_OK )
547 return 0;
548
549 return RepeatLayout( aEvent, refRAs.front() );
550}
551
552
554{
555 m_areas.m_refRA = nullptr;
556
557 for( RULE_AREA& ra : m_areas.m_areas )
558 {
559 if( ra.m_zone == aRefZone )
560 {
561 m_areas.m_refRA = &ra;
562 break;
563 }
564 }
565
566 if( !m_areas.m_refRA )
567 return -1;
568
569 m_areas.m_compatMap.clear();
570
571 for( RULE_AREA& ra : m_areas.m_areas )
572 {
573 if( ra.m_zone == m_areas.m_refRA->m_zone )
574 continue;
575
576 m_areas.m_compatMap[&ra] = RULE_AREA_COMPAT_DATA();
577
578 resolveConnectionTopology( m_areas.m_refRA, &ra, m_areas.m_compatMap[&ra] );
579 }
580
581 return 0;
582}
583
584
585int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, RULE_AREA& aRefArea, RULE_AREA& aTargetArea )
586{
588
589 if( !resolveConnectionTopology( &aRefArea, &aTargetArea, compat ) )
590 {
591 if( Pgm().IsGUI() )
592 {
593 wxString summary = wxString::Format( _( "Rule Area topologies do not match: %s" ), compat.m_errorMsg );
595 }
596
597 return -1;
598 }
599
600 BOARD_COMMIT commit( GetManager(), true, false );
601
602 if( !copyRuleAreaContents( &aRefArea, &aTargetArea, &commit, m_areas.m_options, compat ) )
603 {
604 auto errMsg = wxString::Format( _( "Copy Rule Area contents failed between rule areas '%s' and '%s'." ),
605 m_areas.m_refRA->m_zone->GetZoneName(),
606 aTargetArea.m_zone->GetZoneName() );
607
608 commit.Revert();
609
610 if( Pgm().IsGUI() )
611 frame()->ShowInfoBarError( errMsg, true );
612
613 return -1;
614 }
615
617 {
618 if( aTargetArea.m_components.size() == 0 || !( *aTargetArea.m_components.begin() )->GetParentGroup() )
619 {
620 commit.Revert();
621
622 if( Pgm().IsGUI() )
623 frame()->ShowInfoBarError( _( "Target group does not have a group." ), true );
624
625 return -1;
626 }
627
628 EDA_GROUP* group = ( *aTargetArea.m_components.begin() )->GetParentGroup();
629
630 commit.Add( group->AsEdaItem() );
631
632 for( BOARD_ITEM* item : compat.m_groupableItems )
633 {
634 commit.Modify( item );
635 group->AddItem( item );
636 }
637 }
638
639 commit.Push( _( "Repeat layout" ) );
640
641 return 0;
642}
643
644
645int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
646{
647 int totalCopied = 0;
648
649 BOARD_COMMIT commit( GetManager(), true, false );
650
651 for( auto& [targetArea, compatData] : m_areas.m_compatMap )
652 {
653 if( !compatData.m_doCopy )
654 {
655 wxLogTrace( traceMultichannelTool, wxT( "skipping copy to RA '%s' (disabled in dialog)\n" ),
656 targetArea->m_ruleName );
657 continue;
658 }
659
660 if( !compatData.m_isOk )
661 continue;
662
663 if( !copyRuleAreaContents( m_areas.m_refRA, targetArea, &commit, m_areas.m_options, compatData ) )
664 {
665 auto errMsg = wxString::Format( _( "Copy Rule Area contents failed between rule areas '%s' and '%s'." ),
666 m_areas.m_refRA->m_zone->GetZoneName(),
667 targetArea->m_zone->GetZoneName() );
668
669 commit.Revert();
670
671 if( Pgm().IsGUI() )
672 frame()->ShowInfoBarError( errMsg, true );
673
674 return -1;
675 }
676
677 totalCopied++;
678 wxSafeYield();
679 }
680
681 if( m_areas.m_options.m_groupItems )
682 {
683 for( const auto& [targetArea, compatData] : m_areas.m_compatMap )
684 {
685 pruneExistingGroups( commit, compatData.m_affectedItems );
686
687 PCB_GROUP* group = new PCB_GROUP( board() );
688
689 commit.Add( group );
690
691 for( BOARD_ITEM* item : compatData.m_groupableItems )
692 {
693 commit.Modify( item );
694 group->AddItem( item );
695 }
696 }
697 }
698
699 commit.Push( _( "Repeat layout" ) );
700
701 if( Pgm().IsGUI() )
702 frame()->ShowInfoBarMsg( wxString::Format( _( "Copied to %d Rule Areas." ), totalCopied ), true );
703
704 return 0;
705}
706
707
708wxString MULTICHANNEL_TOOL::stripComponentIndex( const wxString& aRef ) const
709{
710 wxString rv;
711
712 // fixme: i'm pretty sure this can be written in a simpler way, but I really suck at figuring
713 // out which wx's built in functions would do it for me. And I hate regexps :-)
714 for( auto k : aRef )
715 {
716 if( !k.IsAscii() )
717 break;
718
719 char c;
720 k.GetAsChar( &c );
721
722 if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c == '_' ) )
723 rv.Append( k );
724 else
725 break;
726 }
727
728 return rv;
729}
730
731
732int MULTICHANNEL_TOOL::findRoutingInRuleArea( RULE_AREA* aRuleArea, std::set<BOARD_CONNECTED_ITEM*>& aOutput,
733 std::shared_ptr<CONNECTIVITY_DATA> aConnectivity,
734 const SHAPE_POLY_SET& aRAPoly, const REPEAT_LAYOUT_OPTIONS& aOpts ) const
735{
736 if( !aRuleArea || !aRuleArea->m_zone )
737 return 0;
738
739 // The user also will consider tracks and vias that are inside the source area but
740 // not connected to any of the source pads to count as "routing" (e.g. stitching vias)
741
742 int count = 0;
743
744 // When we're copying the layout of a design block, we are provided an exact list of items
745 // rather than querying the board for items that are inside the area.
747 {
748 // Get all board connected items that are from the design block, except pads,
749 // which shouldn't be copied
750 for( EDA_ITEM* item : aRuleArea->m_designBlockItems )
751 {
752 // Include any connected items except pads.
753 if( item->Type() == PCB_PAD_T )
754 continue;
755
756 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
757 {
758 if( bci->IsConnected() )
759 aOutput.insert( bci );
760 }
761 }
762
763 return (int) aOutput.size();
764 }
765
767 PCBEXPR_UCODE ucode;
768 PCBEXPR_CONTEXT ctx, preflightCtx;
769
770 auto reportError =
771 [&]( const wxString& aMessage, int aOffset )
772 {
773 wxLogTrace( traceMultichannelTool, wxT( "ERROR: %s" ), aMessage );
774 };
775
776 ctx.SetErrorCallback( reportError );
777 preflightCtx.SetErrorCallback( reportError );
778 compiler.SetErrorCallback( reportError );
779
780 bool restoreBlankName = false;
781
782 if( aRuleArea->m_zone->GetZoneName().IsEmpty() )
783 {
784 restoreBlankName = true;
785 aRuleArea->m_zone->SetZoneName( aRuleArea->m_zone->m_Uuid.AsString() );
786 }
787
788 wxString ruleText = wxString::Format( wxT( "A.enclosedByArea('%s')" ), aRuleArea->m_zone->GetZoneName() );
789
790 auto testAndAdd =
791 [&]( BOARD_CONNECTED_ITEM* aItem )
792 {
793 if( aOutput.contains( aItem ) )
794 return;
795
796 ctx.SetItems( aItem, aItem );
797 LIBEVAL::VALUE* val = ucode.Run( &ctx );
798
799 if( val->AsDouble() != 0.0 )
800 {
801 aOutput.insert( aItem );
802 count++;
803 }
804 };
805
806 if( compiler.Compile( ruleText, &ucode, &preflightCtx ) )
807 {
808 for( PCB_TRACK* track : board()->Tracks() )
809 testAndAdd( track );
810
811 for( BOARD_ITEM* drawing : board()->Drawings() )
812 {
813 if( drawing->IsConnected() )
814 testAndAdd( static_cast<BOARD_CONNECTED_ITEM*>( drawing ) );
815 }
816 }
817
818 if( restoreBlankName )
819 aRuleArea->m_zone->SetZoneName( wxEmptyString );
820
821 return count;
822}
823
824
826 BOARD_COMMIT* aCommit, REPEAT_LAYOUT_OPTIONS aOpts,
827 RULE_AREA_COMPAT_DATA& aCompatData )
828{
829 // copy RA shapes first
830 SHAPE_LINE_CHAIN refOutline = aRefArea->m_zone->Outline()->COutline( 0 );
831 SHAPE_LINE_CHAIN targetOutline = aTargetArea->m_zone->Outline()->COutline( 0 );
832
833 FOOTPRINT* targetAnchorFp = nullptr;
834 VECTOR2I disp = aTargetArea->m_center - aRefArea->m_center;
835 EDA_ANGLE rot = EDA_ANGLE( 0 );
836
837 if( aOpts.m_anchorFp )
838 {
839 for( const auto& [refFP, targetFP] : aCompatData.m_matchingComponents )
840 {
841 if( refFP->GetReference() == aOpts.m_anchorFp->GetReference() )
842 targetAnchorFp = targetFP;
843 }
844
845 if( targetAnchorFp )
846 {
847 VECTOR2I oldpos = aOpts.m_anchorFp->GetPosition();
848 rot = EDA_ANGLE( targetAnchorFp->GetOrientationDegrees() - aOpts.m_anchorFp->GetOrientationDegrees() );
849 aOpts.m_anchorFp->Rotate( VECTOR2( 0, 0 ), EDA_ANGLE( rot ) );
850 oldpos = aOpts.m_anchorFp->GetPosition();
851 VECTOR2I newpos = targetAnchorFp->GetPosition();
852 disp = newpos - oldpos;
853 aOpts.m_anchorFp->Rotate( VECTOR2( 0, 0 ), EDA_ANGLE( -rot ) );
854 }
855 }
856
857 SHAPE_POLY_SET refPoly;
858 refPoly.AddOutline( refOutline );
859 refPoly.CacheTriangulation( false );
860
861 SHAPE_POLY_SET targetPoly;
862
863 SHAPE_LINE_CHAIN newTargetOutline( refOutline );
864 newTargetOutline.Rotate( rot, VECTOR2( 0, 0 ) );
865 newTargetOutline.Move( disp );
866 targetPoly.AddOutline( newTargetOutline );
867 targetPoly.CacheTriangulation( false );
868
869 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
870 std::map<EDA_GROUP*, EDA_GROUP*> groupMap;
871
872 auto fixupParentGroup =
873 [&]( BOARD_ITEM* sourceItem, BOARD_ITEM* destItem )
874 {
875 if( EDA_GROUP* parentGroup = sourceItem->GetParentGroup() )
876 {
877 if( !groupMap.contains( parentGroup ) )
878 {
879 BOARD_ITEM* newGroup = static_cast<PCB_GROUP*>( parentGroup->AsEdaItem() )->Duplicate( false );
880 groupMap[parentGroup] = static_cast<PCB_GROUP*>( newGroup );
881 aCommit->Add( newGroup );
882 }
883
884 destItem->SetParentGroup( groupMap[parentGroup] );
885 }
886 };
887
888 // Only stage changes for a target Rule Area zone if it actually belongs to the board.
889 // In some workflows (e.g. ApplyDesignBlockLayout), the target area is a temporary zone
890 // and is not added to the BOARD.
891 bool targetZoneOnBoard = false;
892
893 if( aTargetArea->m_zone )
894 {
895 for( ZONE* z : board()->Zones() )
896 {
897 if( z == aTargetArea->m_zone )
898 {
899 targetZoneOnBoard = true;
900 break;
901 }
902 }
903 }
904
905 if( targetZoneOnBoard )
906 {
907 aCommit->Modify( aTargetArea->m_zone );
908 aCompatData.m_affectedItems.insert( aTargetArea->m_zone );
909 aCompatData.m_groupableItems.insert( aTargetArea->m_zone );
910 }
911
912 if( aOpts.m_copyRouting )
913 {
914 std::set<BOARD_CONNECTED_ITEM*> refRouting;
915 std::set<BOARD_CONNECTED_ITEM*> targetRouting;
916
917 wxLogTrace( traceMultichannelTool, wxT( "copying routing: %d fps\n" ),
918 (int) aCompatData.m_matchingComponents.size() );
919
920 std::set<int> refc;
921 std::set<int> targc;
922
923 for( const auto& [refFP, targetFP] : aCompatData.m_matchingComponents )
924 {
925 for( PAD* pad : refFP->Pads() )
926 refc.insert( pad->GetNetCode() );
927
928 for( PAD* pad : targetFP->Pads() )
929 targc.insert( pad->GetNetCode() );
930 }
931
932 findRoutingInRuleArea( aTargetArea, targetRouting, connectivity, targetPoly, aOpts );
933 findRoutingInRuleArea( aRefArea, refRouting, connectivity, refPoly, aOpts );
934
935 for( BOARD_CONNECTED_ITEM* item : targetRouting )
936 {
937 // Never remove pads as part of routing copy.
938 if( item->Type() == PCB_PAD_T )
939 continue;
940
941 if( item->IsLocked() && !aOpts.m_includeLockedItems )
942 continue;
943
944 if( aOpts.m_connectedRoutingOnly && !targc.contains( item->GetNetCode() ) )
945 continue;
946
947 // item already removed
948 if( aCommit->GetStatus( item ) != 0 )
949 continue;
950
951 if( aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
952 {
953 aCompatData.m_affectedItems.insert( item );
954 aCommit->Remove( item );
955 }
956 }
957
958 for( BOARD_CONNECTED_ITEM* item : refRouting )
959 {
960 // Never copy pads as part of routing copy.
961 if( item->Type() == PCB_PAD_T )
962 continue;
963
964 if( item->IsLocked() && !aOpts.m_includeLockedItems )
965 continue;
966
967 if( aOpts.m_connectedRoutingOnly && !refc.contains( item->GetNetCode() ) )
968 continue;
969
970 if( !aRefArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
971 continue;
972
973 if( !aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
974 continue;
975
976 BOARD_CONNECTED_ITEM* copied = static_cast<BOARD_CONNECTED_ITEM*>( item->Duplicate( false ) );
977
978 fixupNet( item, copied, aCompatData.m_matchingComponents );
979 fixupParentGroup( item, copied );
980
981 copied->Rotate( VECTOR2( 0, 0 ), rot );
982 copied->Move( disp );
983 aCompatData.m_groupableItems.insert( copied );
984 aCommit->Add( copied );
985 }
986 }
987
988 if( aOpts.m_copyOtherItems )
989 {
990 std::set<BOARD_ITEM*> sourceItems;
991 std::set<BOARD_ITEM*> targetItems;
992
993 findOtherItemsInRuleArea( aRefArea, sourceItems );
994 findOtherItemsInRuleArea( aTargetArea, targetItems );
995
996 for( BOARD_ITEM* item : targetItems )
997 {
998 if( item->Type() == PCB_TEXT_T && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
999 continue;
1000
1001 if( item->IsLocked() && !aOpts.m_includeLockedItems )
1002 continue;
1003
1004 // item already removed
1005 if( aCommit->GetStatus( item ) != 0 )
1006 continue;
1007
1008 if( item->Type() == PCB_ZONE_T )
1009 {
1010 ZONE* zone = static_cast<ZONE*>( item );
1011
1012 // Check all zone layers are included in the rule area
1013 bool layerMismatch = false;
1014 LSET zoneLayers = zone->GetLayerSet();
1015
1016 for( const PCB_LAYER_ID& layer : zoneLayers )
1017 {
1018 if( !aTargetArea->m_zone->GetLayerSet().Contains( layer ) )
1019 layerMismatch = true;
1020 }
1021
1022 if( !layerMismatch )
1023 {
1024 aCompatData.m_affectedItems.insert( zone );
1025 aCommit->Remove( zone );
1026 }
1027 }
1028 else
1029 {
1030 if( aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
1031 {
1032 aCompatData.m_affectedItems.insert( item );
1033 aCommit->Remove( item );
1034 }
1035 }
1036 }
1037
1038 for( BOARD_ITEM* item : sourceItems )
1039 {
1040 if( item->Type() == PCB_TEXT_T && item->GetParent() && item->GetParent()->Type() == PCB_FOOTPRINT_T )
1041 continue;
1042
1043 if( item->IsLocked() && !aOpts.m_includeLockedItems )
1044 continue;
1045
1046 BOARD_ITEM* copied = nullptr;
1047
1048 if( item->Type() == PCB_ZONE_T )
1049 {
1050 ZONE* zone = static_cast<ZONE*>( item );
1051
1052 // Check all zone layers are included in the rule area
1053 bool layerMismatch = false;
1054 LSET zoneLayers = zone->GetLayerSet();
1055
1056 for( const PCB_LAYER_ID& layer : zoneLayers )
1057 {
1058 if( !aRefArea->m_zone->GetLayerSet().Contains( layer )
1059 || !aTargetArea->m_zone->GetLayerSet().Contains( layer ) )
1060 {
1061 layerMismatch = true;
1062 }
1063 }
1064
1065 if( layerMismatch )
1066 continue;
1067
1068 ZONE* targetZone = static_cast<ZONE*>( item->Duplicate( false ) );
1069 fixupNet( zone, targetZone, aCompatData.m_matchingComponents );
1070
1071 copied = targetZone;
1072 }
1073 else
1074 {
1075 if( !aRefArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
1076 continue;
1077
1078 if( !aTargetArea->m_zone->GetLayerSet().Contains( item->GetLayer() ) )
1079 continue;
1080
1081 copied = static_cast<BOARD_ITEM*>( item->Clone() );
1082 }
1083
1084 if( copied )
1085 {
1086 fixupParentGroup( item, copied );
1087
1088 copied->ClearFlags();
1089 copied->Rotate( VECTOR2( 0, 0 ), rot );
1090 copied->Move( disp );
1091 aCompatData.m_groupableItems.insert( copied );
1092 aCommit->Add( copied );
1093 }
1094 }
1095 }
1096
1097 if( aOpts.m_copyPlacement )
1098 {
1099 for( const auto& [refFP, targetFP] : aCompatData.m_matchingComponents )
1100 {
1101 if( !aRefArea->m_zone->GetLayerSet().Contains( refFP->GetLayer() ) )
1102 {
1103 wxLogTrace( traceMultichannelTool, wxT( "discard ref:%s (ref layer)\n" ),
1104 refFP->GetReference() );
1105 continue;
1106 }
1107 if( !aTargetArea->m_zone->GetLayerSet().Contains( refFP->GetLayer() ) )
1108 {
1109 wxLogTrace( traceMultichannelTool, wxT( "discard ref:%s (target layer)\n" ),
1110 refFP->GetReference() );
1111 continue;
1112 }
1113
1114 // Ignore footprints outside of the rule area
1115 if( !refFP->GetEffectiveShape( refFP->GetLayer() )->Collide( &refPoly, 0 ) )
1116 continue;
1117
1118 if( targetFP->IsLocked() && !aOpts.m_includeLockedItems )
1119 continue;
1120
1121 aCommit->Modify( targetFP );
1122
1123 targetFP->SetLayerAndFlip( refFP->GetLayer() );
1124 targetFP->SetOrientation( refFP->GetOrientation() );
1125 targetFP->SetPosition( refFP->GetPosition() );
1126 targetFP->Rotate( VECTOR2( 0, 0 ), rot );
1127 targetFP->Move( disp );
1128
1129 for( PCB_FIELD* refField : refFP->GetFields() )
1130 {
1131 PCB_FIELD* targetField = targetFP->GetField( refField->GetName() );
1132 wxCHECK2( targetField, continue );
1133
1134 targetField->SetLayerSet( refField->GetLayerSet() );
1135 targetField->SetVisible( refField->IsVisible() );
1136 targetField->SetAttributes( refField->GetAttributes() );
1137 targetField->SetPosition( refField->GetPosition() );
1138 targetField->Rotate( VECTOR2( 0, 0 ), rot );
1139 targetField->Move( disp );
1140 targetField->SetIsKnockout( refField->IsKnockout() );
1141 }
1142
1143 aCompatData.m_affectedItems.insert( targetFP );
1144 aCompatData.m_groupableItems.insert( targetFP );
1145 }
1146 }
1147
1148 aTargetArea->m_zone->RemoveAllContours();
1149 aTargetArea->m_zone->AddPolygon( newTargetOutline );
1150 aTargetArea->m_zone->UnHatchBorder();
1151 aTargetArea->m_zone->HatchBorder();
1152
1153 return true;
1154}
1155
1161 TMATCH::COMPONENT_MATCHES& aComponentMatches )
1162{
1163 auto connectivity = board()->GetConnectivity();
1164 const std::vector<BOARD_CONNECTED_ITEM*> refConnectedPads = connectivity->GetNetItems( aRef->GetNetCode(),
1165 { PCB_PAD_T } );
1166
1167 for( const BOARD_CONNECTED_ITEM* refConItem : refConnectedPads )
1168 {
1169 if( refConItem->Type() != PCB_PAD_T )
1170 continue;
1171
1172 const PAD* refPad = static_cast<const PAD*>( refConItem );
1173 FOOTPRINT* sourceFootprint = refPad->GetParentFootprint();
1174
1175 if( aComponentMatches.contains( sourceFootprint ) )
1176 {
1177 const FOOTPRINT* targetFootprint = aComponentMatches[sourceFootprint];
1178 std::vector<const PAD*> targetFpPads = targetFootprint->GetPads( refPad->GetNumber() );
1179
1180 if( !targetFpPads.empty() )
1181 {
1182 int targetNetCode = targetFpPads[0]->GetNet()->GetNetCode();
1183 aTarget->SetNetCode( targetNetCode );
1184
1185 break;
1186 }
1187 }
1188 }
1189}
1190
1191
1193 RULE_AREA_COMPAT_DATA& aMatches )
1194{
1195 using namespace TMATCH;
1196
1197 std::unique_ptr<CONNECTION_GRAPH> cgRef ( CONNECTION_GRAPH::BuildFromFootprintSet( aRefArea->m_components ) );
1198 std::unique_ptr<CONNECTION_GRAPH> cgTarget ( CONNECTION_GRAPH::BuildFromFootprintSet( aTargetArea->m_components ) );
1199
1200 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> mismatchReasons;
1201 bool status = cgRef->FindIsomorphism( cgTarget.get(), aMatches.m_matchingComponents, mismatchReasons );
1202
1203 aMatches.m_isOk = status;
1204
1205 if( status )
1206 {
1207 aMatches.m_errorMsg = _( "OK" );
1208 aMatches.m_mismatchReasons.clear();
1209 return true;
1210 }
1211
1212 aMatches.m_mismatchReasons.clear();
1213
1214 for( const auto& reason : mismatchReasons )
1215 {
1216 if( reason.m_reason.IsEmpty() )
1217 continue;
1218
1219 if( !reason.m_reference.IsEmpty() && !reason.m_candidate.IsEmpty() )
1220 {
1221 aMatches.m_mismatchReasons.push_back( wxString::Format( wxT( "%s -> %s: %s" ),
1222 reason.m_reference,
1223 reason.m_candidate,
1224 reason.m_reason ) );
1225 }
1226 else if( !reason.m_reference.IsEmpty() )
1227 {
1228 aMatches.m_mismatchReasons.push_back( wxString::Format( wxT( "%s: %s" ),
1229 reason.m_reference,
1230 reason.m_reason ) );
1231 }
1232 else
1233 aMatches.m_mismatchReasons.push_back( reason.m_reason );
1234 }
1235
1236 if( aMatches.m_mismatchReasons.empty() )
1237 aMatches.m_mismatchReasons.push_back( _( "Topology mismatch" ) );
1238
1239 aMatches.m_errorMsg = aMatches.m_mismatchReasons.front();
1240
1241 return status;
1242}
1243
1244
1246 const std::unordered_set<BOARD_ITEM*>& aItemsToRemove )
1247{
1248 // Note: groups are only collections, not "real" hierarchy. A group's members are still parented
1249 // by the board (and therefore nested groups are still in the board's list of groups).
1250 for( PCB_GROUP* group : board()->Groups() )
1251 {
1252 std::vector<EDA_ITEM*> pruneList;
1253
1254 for( EDA_ITEM* refItem : group->GetItems() )
1255 {
1256 for( BOARD_ITEM* testItem : aItemsToRemove )
1257 {
1258 if( refItem->m_Uuid == testItem->m_Uuid )
1259 pruneList.push_back( refItem );
1260 }
1261 }
1262
1263 if( !pruneList.empty() )
1264 {
1265 aCommit.Modify( group );
1266
1267 for( EDA_ITEM* item : pruneList )
1268 group->RemoveItem( item );
1269
1270 if( group->GetItems().empty() )
1271 aCommit.Remove( group );
1272 }
1273 }
1274
1275 return false;
1276}
1277
1278
1280{
1281 if( Pgm().IsGUI() )
1282 {
1284
1285 if( m_areas.m_areas.size() <= 1 )
1286 {
1287 frame()->ShowInfoBarError( _( "Cannot auto-generate any placement areas because the "
1288 "schematic has only one or no hierarchical sheets, "
1289 "groups, or component classes." ),
1290 true );
1291 return 0;
1292 }
1293
1295 int ret = dialog.ShowModal();
1296
1297 if( ret != wxID_OK )
1298 return 0;
1299 }
1300
1301 for( ZONE* zone : board()->Zones() )
1302 {
1303 if( !zone->GetIsRuleArea() )
1304 continue;
1305
1306 if( !zone->GetPlacementAreaEnabled() )
1307 continue;
1308
1309 std::set<FOOTPRINT*> components;
1310 RULE_AREA zoneRA;
1311 zoneRA.m_zone = zone;
1312 zoneRA.m_sourceType = zone->GetPlacementAreaSourceType();
1313 findComponentsInRuleArea( &zoneRA, components );
1314
1315 if( components.empty() )
1316 continue;
1317
1318 for( RULE_AREA& ra : m_areas.m_areas )
1319 {
1320 if( components == ra.m_components )
1321 {
1322 if( zone->GetPlacementAreaSourceType() == PLACEMENT_SOURCE_T::SHEETNAME )
1323 {
1324 wxLogTrace( traceMultichannelTool,
1325 wxT( "Placement rule area for sheet '%s' already exists as '%s'\n" ),
1326 ra.m_sheetPath, zone->GetZoneName() );
1327 }
1328 else if( zone->GetPlacementAreaSourceType() == PLACEMENT_SOURCE_T::COMPONENT_CLASS )
1329 {
1330 wxLogTrace( traceMultichannelTool,
1331 wxT( "Placement rule area for component class '%s' already exists as '%s'\n" ),
1332 ra.m_componentClass, zone->GetZoneName() );
1333 }
1334 else
1335 {
1336 wxLogTrace( traceMultichannelTool,
1337 wxT( "Placement rule area for group '%s' already exists as '%s'\n" ),
1338 ra.m_groupName, zone->GetZoneName() );
1339 }
1340
1341 ra.m_oldZone = zone;
1342 ra.m_existsAlready = true;
1343 }
1344 }
1345 }
1346
1347 wxLogTrace( traceMultichannelTool, wxT( "%d placement areas found\n" ), (int) m_areas.m_areas.size() );
1348
1349 BOARD_COMMIT commit( GetManager(), true, false );
1350
1351 for( RULE_AREA& ra : m_areas.m_areas )
1352 {
1353 if( !ra.m_generateEnabled )
1354 continue;
1355
1356 if( ra.m_existsAlready && !m_areas.m_replaceExisting )
1357 continue;
1358
1359 if( ra.m_components.empty() )
1360 continue;
1361
1362 SHAPE_LINE_CHAIN raOutline = buildRAOutline( ra.m_components, 100000 );
1363
1364 std::unique_ptr<ZONE> newZone( new ZONE( board() ) );
1365
1367 newZone->SetZoneName( wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_sheetPath ) );
1369 newZone->SetZoneName( wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_componentClass ) );
1370 else
1371 newZone->SetZoneName( wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_groupName ) );
1372
1373 wxLogTrace( traceMultichannelTool, wxT( "Generated rule area '%s' (%d components)\n" ),
1374 newZone->GetZoneName(),
1375 (int) ra.m_components.size() );
1376
1377 newZone->SetIsRuleArea( true );
1378 newZone->SetLayerSet( LSET::AllCuMask() );
1379 newZone->SetPlacementAreaEnabled( true );
1380 newZone->SetDoNotAllowZoneFills( false );
1381 newZone->SetDoNotAllowVias( false );
1382 newZone->SetDoNotAllowTracks( false );
1383 newZone->SetDoNotAllowPads( false );
1384 newZone->SetDoNotAllowFootprints( false );
1385
1387 {
1388 newZone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::SHEETNAME );
1389 newZone->SetPlacementAreaSource( ra.m_sheetPath );
1390 }
1392 {
1393 newZone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::COMPONENT_CLASS );
1394 newZone->SetPlacementAreaSource( ra.m_componentClass );
1395 }
1396 else
1397 {
1398 newZone->SetPlacementAreaSourceType( PLACEMENT_SOURCE_T::GROUP_PLACEMENT );
1399 newZone->SetPlacementAreaSource( ra.m_groupName );
1400 }
1401
1402 newZone->AddPolygon( raOutline );
1403 newZone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
1404
1405 if( ra.m_existsAlready )
1406 {
1407 commit.Remove( ra.m_oldZone );
1408 }
1409
1410 ra.m_zone = newZone.release();
1411 commit.Add( ra.m_zone );
1412 }
1413
1414 // fixme: handle corner cases where the items belonging to a Rule Area already
1415 // belong to other groups.
1416
1417 if( m_areas.m_options.m_groupItems )
1418 {
1419 for( RULE_AREA& ra : m_areas.m_areas )
1420 {
1421 if( !ra.m_generateEnabled )
1422 continue;
1423
1424 if( ra.m_existsAlready && !m_areas.m_replaceExisting )
1425 continue;
1426
1427 std::unordered_set<BOARD_ITEM*> toPrune;
1428
1429 std::copy( ra.m_components.begin(), ra.m_components.end(), std::inserter( toPrune, toPrune.begin() ) );
1430
1431 if( ra.m_existsAlready )
1432 toPrune.insert( ra.m_zone );
1433
1434 pruneExistingGroups( commit, toPrune );
1435
1436 PCB_GROUP* group = new PCB_GROUP( board() );
1437
1438 commit.Add( group );
1439
1440 commit.Modify( ra.m_zone );
1441 group->AddItem( ra.m_zone );
1442
1443 for( FOOTPRINT* fp : ra.m_components )
1444 {
1445 commit.Modify( fp );
1446 group->AddItem( fp );
1447 }
1448 }
1449 }
1450
1451 commit.Push( _( "Auto-generate placement rule areas" ) );
1452
1453 return true;
1454}
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:83
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
virtual void SetLayerSet(const LSET &aLayers)
Definition board_item.h:264
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:324
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
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:116
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition eda_text.cpp:441
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:394
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
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:55
const wxString & GetNumber() const
Definition pad.h:137
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:84
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition pcb_text.h:86
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition pcb_text.cpp:401
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:1118
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:1245
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:1239
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)
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