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