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 (C) Kicad Developers, see change_log.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>
41#include <optional>
42#include <algorithm>
44#include <random>
45#include <core/profile.h>
46#include <wx/log.h>
47#include <pgm_base.h>
48
49
50#define MULTICHANNEL_EXTRA_DEBUG
51
52static const wxString traceMultichannelTool = wxT( "MULTICHANNEL_TOOL" );
53
54
56{
57}
58
59
61{
62
63}
64
65
67{
70}
71
72
74 std::set<FOOTPRINT*>& aComponents )
75{
77 PCBEXPR_UCODE ucode;
78 PCBEXPR_CONTEXT ctx, preflightCtx;
79
80 auto reportError = [&]( const wxString& aMessage, int aOffset )
81 {
82 wxLogTrace( traceMultichannelTool, wxT( "ERROR: %s"), aMessage );
83 };
84
85 ctx.SetErrorCallback( reportError );
86 preflightCtx.SetErrorCallback( reportError );
87 compiler.SetErrorCallback( reportError );
88 //compiler.SetDebugReporter( m_reporter );
89
90 wxLogTrace( traceMultichannelTool, wxT( "rule area '%s'"), aRuleArea->GetZoneName() );
91
92 wxString ruleText;
93
94 switch( aRuleArea->GetRuleAreaPlacementSourceType() )
95 {
96 case RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME:
97 {
98 ruleText =
99 wxT( "A.memberOfSheet('" ) + aRuleArea->GetRuleAreaPlacementSource() + wxT( "')" );
100 break;
101 }
102 case RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS:
103 ruleText = wxT( "A.hasComponentClass('" ) + aRuleArea->GetRuleAreaPlacementSource()
104 + wxT( "')" );
105 break;
106 }
107
108 auto ok = compiler.Compile( ruleText, &ucode, &preflightCtx );
109
110 if( !ok )
111 {
112 return false;
113 }
114
115 for( FOOTPRINT* fp : board()->Footprints() )
116 {
117 ctx.SetItems( fp, fp );
118 auto val = ucode.Run( &ctx );
119 if( val->AsDouble() != 0.0 )
120 {
121 wxLogTrace( traceMultichannelTool, wxT( " - %s [sheet %s]" ), fp->GetReference(),
122 fp->GetSheetname() );
123
124 aComponents.insert( fp );
125 }
126 }
127
128 return true;
129}
130
131
132std::set<FOOTPRINT*> MULTICHANNEL_TOOL::queryComponentsInSheet( wxString aSheetName ) const
133{
134 std::set<FOOTPRINT*> rv;
135 if( aSheetName.EndsWith( wxT( "/" ) ) )
136 aSheetName.RemoveLast();
137
138 for( auto& fp : board()->Footprints() )
139 {
140 auto sn = fp->GetSheetname();
141 if( sn.EndsWith( wxT( "/" ) ) )
142 sn.RemoveLast();
143
144 if( sn == aSheetName )
145 {
146 rv.insert( fp );
147 }
148 }
149
150 return rv;
151}
152
153
154std::set<FOOTPRINT*>
155MULTICHANNEL_TOOL::queryComponentsInComponentClass( const wxString& aComponentClassName ) const
156{
157 std::set<FOOTPRINT*> rv;
158
159 for( auto& fp : board()->Footprints() )
160 {
161 if( fp->GetComponentClass()->ContainsClassName( aComponentClassName ) )
162 rv.insert( fp );
163 }
164
165 return rv;
166}
167
168
169const SHAPE_LINE_CHAIN MULTICHANNEL_TOOL::buildRAOutline( std::set<FOOTPRINT*>& aFootprints,
170 int aMargin )
171{
172 std::vector<VECTOR2I> bbCorners;
173 bbCorners.reserve( aFootprints.size() * 4 );
174
175 for( auto fp : aFootprints )
176 {
177 const BOX2I bb = fp->GetBoundingBox( false ).GetInflated( aMargin );
178 KIGEOM::CollectBoxCorners( bb, bbCorners );
179 }
180
181 std::vector<VECTOR2I> hullVertices;
182 BuildConvexHull( hullVertices, bbCorners );
183
184 SHAPE_LINE_CHAIN hull( hullVertices );
185
186 // Make the newly computed convex hull use only 90 degree segments
187 return KIGEOM::RectifyPolygon( hull );
188}
189
190
192{
193 using PathAndName = std::pair<wxString, wxString>;
194 std::set<PathAndName> uniqueSheets;
195 std::set<wxString> uniqueComponentClasses;
196
197 m_areas.m_areas.clear();
198
199 for( const FOOTPRINT* fp : board()->Footprints() )
200 {
201 uniqueSheets.insert( PathAndName( fp->GetSheetname(), fp->GetSheetfile() ) );
202
203 const COMPONENT_CLASS* compClass = fp->GetComponentClass();
204
205 for( const COMPONENT_CLASS* singleClass : compClass->GetConstituentClasses() )
206 uniqueComponentClasses.insert( singleClass->GetName() );
207 }
208
209 for( const PathAndName& sheet : uniqueSheets )
210 {
211 RULE_AREA ent;
212
213 ent.m_sourceType = RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME;
214 ent.m_generateEnabled = false;
215 ent.m_sheetPath = sheet.first;
216 ent.m_sheetName = sheet.second;
218 m_areas.m_areas.push_back( ent );
219
220 wxLogTrace( traceMultichannelTool, wxT("found sheet '%s' @ '%s' s %d\n"),
221 ent.m_sheetName, ent.m_sheetPath, (int)m_areas.m_areas.size() );
222 }
223
224 for( const wxString& compClass : uniqueComponentClasses )
225 {
226 RULE_AREA ent;
227
228 ent.m_sourceType = RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS;
229 ent.m_generateEnabled = false;
230 ent.m_componentClass = compClass;
232 m_areas.m_areas.push_back( ent );
233
234 wxLogTrace( traceMultichannelTool, wxT( "found component class '%s' s %d\n" ),
235 ent.m_componentClass, static_cast<int>( m_areas.m_areas.size() ) );
236 }
237}
238
239
241{
242 m_areas.m_areas.clear();
243
244 for( ZONE* zone : board()->Zones() )
245 {
246 if( !zone->GetIsRuleArea() )
247 continue;
248 if( !zone->GetRuleAreaPlacementEnabled() )
249 continue;
250
251 RULE_AREA area;
252
253 area.m_existsAlready = true;
254 area.m_area = zone;
255
257
258 area.m_ruleName = zone->GetZoneName();
259 area.m_center = zone->Outline()->COutline( 0 ).Centre();
260 m_areas.m_areas.push_back( area );
261
262 wxLogTrace( traceMultichannelTool, wxT("RA '%s', %d footprints\n"), area.m_ruleName, (int) area.m_raFootprints.size() );
263 }
264
265 wxLogTrace( traceMultichannelTool, wxT("Total RAs found: %d\n"), (int) m_areas.m_areas.size() );
266}
267
268
270{
271 for( RULE_AREA& ra : m_areas.m_areas )
272 {
273 if( ra.m_ruleName == aName )
274 return &ra;
275 }
276
277 return nullptr;
278}
279
280
282{
283 std::vector<ZONE*> refRAs;
284
285 auto isSelectedItemAnRA = [] ( EDA_ITEM* aItem ) -> ZONE*
286 {
287 if( aItem->Type() != PCB_ZONE_T )
288 return nullptr;
289 ZONE* zone = static_cast<ZONE*>( aItem );
290 if( !zone->GetIsRuleArea() )
291 return nullptr;
292 if( !zone->GetRuleAreaPlacementEnabled() )
293 return nullptr;
294 return zone;
295 };
296
297 for( EDA_ITEM* item : selection() )
298 {
299 if( auto zone = isSelectedItemAnRA( item ) )
300 {
301 refRAs.push_back(zone);
302 }
303 else if ( item->Type() == PCB_GROUP_T )
304 {
305 PCB_GROUP *group = static_cast<PCB_GROUP*>( item );
306 for( BOARD_ITEM* grpItem : group->GetItems() )
307 {
308 if( auto grpZone = isSelectedItemAnRA( grpItem ) )
309 {
310 refRAs.push_back( grpZone );
311 }
312 }
313 }
314 }
315
316 if( refRAs.size() != 1 )
317 {
318 frame()->ShowInfoBarError( _( "Please select a single reference Rule Area to copy from." ),
319 true );
320 return 0;
321 }
322
324
325 int status = CheckRACompatibility( refRAs.front() );
326
327 if( status < 0 )
328 return status;
329
330 if( m_areas.m_areas.size() <= 1 )
331 {
332 frame()->ShowInfoBarError( _( "No Rule Areas to repeat layout to have been found." ),
333 true );
334 return 0;
335 }
336
337
339 int ret = dialog.ShowModal();
340
341 if( ret != wxID_OK )
342 return 0;
343
344 return RepeatLayout( aEvent, refRAs.front() );
345}
346
347
349{
350 m_areas.m_refRA = nullptr;
351
352 for( RULE_AREA& ra : m_areas.m_areas )
353 {
354 if( ra.m_area == aRefZone )
355 {
356 m_areas.m_refRA = &ra;
357 break;
358 }
359 }
360
361 if( !m_areas.m_refRA )
362 return -1;
363
364 m_areas.m_compatMap.clear();
365
366 for( RULE_AREA& ra : m_areas.m_areas )
367 {
368 if( ra.m_area == m_areas.m_refRA->m_area )
369 continue;
370
372
374 }
375
376 return 0;
377}
378
379int MULTICHANNEL_TOOL::RepeatLayout( const TOOL_EVENT& aEvent, ZONE* aRefZone )
380{
381 int totalCopied = 0;
382
383 for( auto& targetArea : m_areas.m_compatMap )
384 {
385 if( !targetArea.second.m_doCopy )
386 {
387 wxLogTrace( traceMultichannelTool, wxT("skipping copy to RA '%s' (disabled in dialog)\n"),
388 targetArea.first->m_ruleName );
389 continue;
390 }
391
392 if( !targetArea.second.m_isOk )
393 continue;
394
395 BOARD_COMMIT commit( GetManager(), true );
396
397 std::unordered_set<BOARD_ITEM*> affectedItems;
398 std::unordered_set<BOARD_ITEM*> groupableItems;
399
400 if( !copyRuleAreaContents( targetArea.second.m_matchingComponents, &commit, m_areas.m_refRA,
401 targetArea.first, m_areas.m_options, affectedItems, groupableItems ) )
402 {
403 auto errMsg = wxString::Format(
404 _( "Copy Rule Area contents failed between rule areas '%s' and '%s'." ),
406 targetArea.first->m_area->GetZoneName() );
407
408 commit.Revert();
409 if( Pgm().IsGUI() )
410 {
411 frame()->ShowInfoBarError( errMsg, true );
412 }
413
414 return -1;
415 }
416
417 commit.Push( _( "Repeat layout" ) );
418
420 {
421 // fixme: sth gets weird when creating new zones & grouping them within a single COMMIT
422 BOARD_COMMIT grpCommit( GetManager(), true );
423
424 pruneExistingGroups( grpCommit, affectedItems );
425
426 PCB_GROUP* grp = new PCB_GROUP( board() );
427
428 grpCommit.Add( grp );
429
430 for( auto item : groupableItems )
431 {
432 grpCommit.Stage( item, CHT_GROUP );
433 }
434
435 grpCommit.Push( _( "Group components with their placement rule areas" ) );
436 }
437
438 totalCopied++;
439 }
440
441 if( Pgm().IsGUI() )
442 {
443 frame()->ShowInfoBarMsg( wxString::Format( _( "Copied to %d Rule Areas." ), totalCopied ),
444 true );
445 }
446 return 0;
447}
448
449
450wxString MULTICHANNEL_TOOL::stripComponentIndex( wxString aRef ) const
451{
452 wxString rv;
453
454 // fixme: i'm pretty sure this can be written in a simpler way, but I really suck at figuring
455 // out which wx's built in functions would do it for me. And I hate regexps :-)
456 for( auto k : aRef )
457 {
458 if( !k.IsAscii() )
459 break;
460 char c;
461 k.GetAsChar( &c );
462
463 if( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || ( c == '_' ) )
464 rv.Append( k );
465 else
466 break;
467 }
468
469 return rv;
470}
471
472
473int MULTICHANNEL_TOOL::findRoutedConnections( std::set<BOARD_ITEM*>& aOutput,
474 std::shared_ptr<CONNECTIVITY_DATA> aConnectivity,
475 const SHAPE_POLY_SET& aRAPoly, RULE_AREA* aRA,
476 FOOTPRINT* aFp,
477 const REPEAT_LAYOUT_OPTIONS& aOpts ) const
478{
479 std::set<BOARD_ITEM*> conns;
480
481 for( PAD* pad : aFp->Pads() )
482 {
483 const std::vector<BOARD_CONNECTED_ITEM*> connItems = aConnectivity->GetConnectedItems(
485
486 for( BOARD_CONNECTED_ITEM* item : connItems )
487 conns.insert( item );
488 }
489
490 int count = 0;
491
492 for( BOARD_ITEM* item : conns )
493 {
494 // fixme: respect layer sets assigned to each RA
495
496 if( item->Type() == PCB_PAD_T )
497 continue;
498
499 std::shared_ptr<SHAPE> effShape = item->GetEffectiveShape( item->GetLayer() );
500
501 if( effShape->Collide( &aRAPoly, 0 ) )
502 {
503 aOutput.insert( item );
504 count++;
505 }
506 }
507
508 return count;
509}
510
511
513 RULE_AREA* aRefArea, RULE_AREA* aTargetArea,
515 std::unordered_set<BOARD_ITEM*>& aAffectedItems,
516 std::unordered_set<BOARD_ITEM*>& aGroupableItems )
517{
518 // copy RA shapes first
519 SHAPE_LINE_CHAIN refOutline = aRefArea->m_area->Outline()->COutline( 0 );
520 SHAPE_LINE_CHAIN targetOutline = aTargetArea->m_area->Outline()->COutline( 0 );
521
522 VECTOR2I disp = aTargetArea->m_center - aRefArea->m_center;
523
524 SHAPE_POLY_SET refPoly;
525 refPoly.AddOutline( refOutline );
526 refPoly.CacheTriangulation( false );
527
528 SHAPE_POLY_SET targetPoly;
529
530 SHAPE_LINE_CHAIN newTargetOutline( refOutline );
531 newTargetOutline.Move( disp );
532 targetPoly.AddOutline( newTargetOutline );
533 targetPoly.CacheTriangulation( false );
534
535 auto connectivity = board()->GetConnectivity();
536
537 aCommit->Modify( aTargetArea->m_area );
538
539 aAffectedItems.insert( aTargetArea->m_area );
540 aGroupableItems.insert( aTargetArea->m_area );
541
542 if( aOpts.m_copyRouting )
543 {
544 std::set<BOARD_ITEM*> refRouting;
545 std::set<BOARD_ITEM*> targetRouting;
546
547 wxLogTrace( traceMultichannelTool, wxT("copying routing: %d fps\n"), (int) aMatches.size() );
548
549 for( auto& fpPair : aMatches )
550 {
551 findRoutedConnections( targetRouting, connectivity, targetPoly, aTargetArea,
552 fpPair.second, aOpts );
553 findRoutedConnections( refRouting, connectivity, refPoly, aRefArea, fpPair.first,
554 aOpts );
555
556 wxLogTrace( traceMultichannelTool, wxT("target-routes %d\n"), (int) targetRouting.size() );
557 }
558
559 for( BOARD_ITEM* item : targetRouting )
560 {
561 if( item->IsLocked() && !aOpts.m_includeLockedItems )
562 continue;
563
564 // item already removed
565 if( aCommit->GetStatus( item ) != 0 )
566 continue;
567
568 if( aTargetArea->m_area->GetLayerSet().Contains( item->GetLayer() ) )
569 {
570 aAffectedItems.insert( item );
571 aCommit->Remove( item );
572 }
573 }
574
575 for( BOARD_ITEM* item : refRouting )
576 {
577 if( !aRefArea->m_area->GetLayerSet().Contains( item->GetLayer() ) )
578 continue;
579 if( !aTargetArea->m_area->GetLayerSet().Contains( item->GetLayer() ) )
580 continue;
581
582 BOARD_ITEM* copied = static_cast<BOARD_ITEM*>( item->Clone() );
583
584 copied->Move( disp );
585 aGroupableItems.insert( copied );
586 aCommit->Add( copied );
587 }
588 }
589
590
591 aTargetArea->m_area->RemoveAllContours();
592 aTargetArea->m_area->AddPolygon( newTargetOutline );
593 aTargetArea->m_area->UnHatchBorder();
594 aTargetArea->m_area->HatchBorder();
595
596 if( aOpts.m_copyPlacement )
597 {
598 for( auto& fpPair : aMatches )
599 {
600 FOOTPRINT* refFP = fpPair.first;
601 FOOTPRINT* targetFP = fpPair.second;
602
603 if( !aRefArea->m_area->GetLayerSet().Contains( refFP->GetLayer() ) )
604 {
605 wxLogTrace( traceMultichannelTool, wxT( "discard ref:%s (ref layer)\n" ),
606 refFP->GetReference() );
607 continue;
608 }
609 if( !aTargetArea->m_area->GetLayerSet().Contains( refFP->GetLayer() ) )
610 {
611 wxLogTrace( traceMultichannelTool, wxT( "discard ref:%s (target layer)\n" ),
612 refFP->GetReference() );
613 continue;
614 }
615
616 if( targetFP->IsLocked() && !aOpts.m_includeLockedItems )
617 continue;
618
619 aCommit->Modify( targetFP );
620
621 targetFP->SetLayerAndFlip( refFP->GetLayer() );
622 targetFP->SetOrientation( refFP->GetOrientation() );
623 VECTOR2I targetPos = refFP->GetPosition() + disp;
624 targetFP->SetPosition( targetPos );
625 targetFP->Reference().SetTextAngle( refFP->Reference().GetTextAngle() );
626 targetFP->Reference().SetPosition( refFP->Reference().GetPosition() + disp );
627 targetFP->Value().SetTextAngle( refFP->Value().GetTextAngle() );
628 targetFP->Value().SetPosition( refFP->Value().GetPosition() + disp );
629
630 aAffectedItems.insert( targetFP );
631 aGroupableItems.insert( targetFP );
632 }
633 }
634
635 return true;
636}
637
638
640{
641 using namespace TMATCH;
642
643 std::unique_ptr<CONNECTION_GRAPH> cgRef ( CONNECTION_GRAPH::BuildFromFootprintSet( aRefArea->m_raFootprints ) );
644 std::unique_ptr<CONNECTION_GRAPH> cgTarget ( CONNECTION_GRAPH::BuildFromFootprintSet( aTargetArea->m_raFootprints ) );
645
646 auto status = cgRef->FindIsomorphism( cgTarget.get(), aMatches.m_matchingComponents );
647
648 switch( status )
649 {
650 case CONNECTION_GRAPH::ST_OK:
651 aMatches.m_isOk = true;
652 aMatches.m_errorMsg = _("OK");
653 break;
654 case CONNECTION_GRAPH::ST_EMPTY:
655 aMatches.m_isOk = false;
656 aMatches.m_errorMsg = _("One or both of the areas has no components assigned.");
657 break;
658 case CONNECTION_GRAPH::ST_COMPONENT_COUNT_MISMATCH:
659 aMatches.m_isOk = false;
660 aMatches.m_errorMsg = _("Component count mismatch");
661 break;
662 case CONNECTION_GRAPH::ST_ITERATION_COUNT_EXCEEDED:
663 aMatches.m_isOk = false;
664 aMatches.m_errorMsg = _("Iteration count exceeded (timeout)");
665 break;
666 case CONNECTION_GRAPH::ST_TOPOLOGY_MISMATCH:
667 aMatches.m_isOk = false;
668 aMatches.m_errorMsg = _("Topology mismatch");
669 break;
670 default:
671 break;
672 }
673
674 return ( status == TMATCH::CONNECTION_GRAPH::ST_OK );
675}
676
677
678bool MULTICHANNEL_TOOL::pruneExistingGroups( COMMIT& aCommit, const std::unordered_set<BOARD_ITEM*>& aItemsToRemove )
679{
680 for( PCB_GROUP* grp : board()->Groups() )
681 {
682 std::unordered_set<BOARD_ITEM*>& grpItems = grp->GetItems();
683 size_t n_erased = 0;
684
685 for( auto refItem : grpItems )
686 {
687 //printf("check ref %p [%s]\n", refItem, refItem->GetTypeDesc().c_str().AsChar() );
688 for ( auto& testItem : aItemsToRemove )
689 {
690
691 if( refItem->m_Uuid == testItem->m_Uuid )
692 {
693 aCommit.Stage( refItem, CHT_UNGROUP );
694 n_erased++;
695 }
696 }
697 }
698
699 if( n_erased == grpItems.size() )
700 {
701 aCommit.Stage( grp, CHT_REMOVE );
702 }
703
704 //printf("Grp %p items %d pruned %d air %d\n", grp,grpItems.size(), (int) n_erased, (int) aItemsToRemove.size() );
705 }
706
707 return false;
708}
709
711{
712 if( Pgm().IsGUI() )
713 {
715
716 if( m_areas.m_areas.size() <= 1 )
717 {
718 frame()->ShowInfoBarError( _( "Cannot auto-generate any placement areas because the "
719 "schematic has only one or no hierarchical sheet(s) or "
720 "component classes." ),
721 true );
722 return 0;
723 }
724
725
727 int ret = dialog.ShowModal();
728
729 if( ret != wxID_OK )
730 return 0;
731 }
732
733 for( ZONE* zone : board()->Zones() )
734 {
735 if( !zone->GetIsRuleArea() )
736 continue;
737 if( !zone->GetRuleAreaPlacementEnabled() )
738 continue;
739
740 std::set<FOOTPRINT*> components;
741 identifyComponentsInRuleArea( zone, components );
742
743 if( components.empty() )
744 continue;
745
746 for( auto& ra : m_areas.m_areas )
747 {
748 if( components == ra.m_components )
749 {
750 if( zone->GetRuleAreaPlacementSourceType()
751 == RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME )
752 {
753 wxLogTrace(
755 wxT( "Placement rule area for sheet '%s' already exists as '%s'\n" ),
756 ra.m_sheetPath, zone->GetZoneName() );
757 }
758 else
759 {
760 wxLogTrace( traceMultichannelTool,
761 wxT( "Placement rule area for component class '%s' already exists "
762 "as '%s'\n" ),
763 ra.m_componentClass, zone->GetZoneName() );
764 }
765
766 ra.m_oldArea = zone;
767 ra.m_existsAlready = true;
768 }
769 }
770 }
771
772 wxLogTrace( traceMultichannelTool,
773 wxT( "%d placement areas found\n" ), (int) m_areas.m_areas.size() );
774
775 BOARD_COMMIT commit( GetManager(), true );
776
777 for( RULE_AREA& ra : m_areas.m_areas )
778 {
779 if( !ra.m_generateEnabled )
780 continue;
782 continue;
783
784 auto raOutline = buildRAOutline( ra.m_components, 100000 );
785
786 std::unique_ptr<ZONE> newZone( new ZONE( board() ) );
787
788 if( ra.m_sourceType == RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME )
789 {
790 newZone->SetZoneName(
791 wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_sheetPath ) );
792 }
793 else
794 {
795 newZone->SetZoneName(
796 wxString::Format( wxT( "auto-placement-area-%s" ), ra.m_componentClass ) );
797 }
798
799 wxLogTrace( traceMultichannelTool, wxT( "Generated rule area '%s' (%d components)\n" ),
800 newZone->GetZoneName(), (int) ra.m_components.size() );
801
802 newZone->SetIsRuleArea( true );
803 newZone->SetLayerSet( LSET::AllCuMask() );
804 newZone->SetRuleAreaPlacementEnabled( true );
805 newZone->SetDoNotAllowCopperPour( false );
806 newZone->SetDoNotAllowVias( false );
807 newZone->SetDoNotAllowTracks( false );
808 newZone->SetDoNotAllowPads( false );
809 newZone->SetDoNotAllowFootprints( false );
810
811 if( ra.m_sourceType == RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME )
812 {
813 newZone->SetRuleAreaPlacementSourceType( RULE_AREA_PLACEMENT_SOURCE_TYPE::SHEETNAME );
814 newZone->SetRuleAreaPlacementSource( ra.m_sheetPath );
815 }
816 else
817 {
818 newZone->SetRuleAreaPlacementSourceType(
819 RULE_AREA_PLACEMENT_SOURCE_TYPE::COMPONENT_CLASS );
820 newZone->SetRuleAreaPlacementSource( ra.m_componentClass );
821 }
822
823 newZone->AddPolygon( raOutline );
824 newZone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
825
826 if( ra.m_existsAlready )
827 {
828 commit.Remove( ra.m_oldArea );
829 }
830
831 ra.m_area = newZone.get();
832 commit.Add( newZone.release() );
833
834 }
835
836 commit.Push( _( "Auto-generate placement rule areas" ) );
837
838 // fixme: handle corner cases where the items belonging to a Rule Area already
839 // belong to other groups.
840
842 {
843 // fixme: sth gets weird when creating new zones & grouping them within a single COMMIT
844 BOARD_COMMIT grpCommit( GetManager(), true );
845
846 for( RULE_AREA& ra : m_areas.m_areas )
847 {
848 if( !ra.m_generateEnabled )
849 continue;
851 continue;
852
853 std::unordered_set<BOARD_ITEM*> toPrune;
854
855 std::copy( ra.m_components.begin(), ra.m_components.end(),
856 std::inserter( toPrune, toPrune.begin() ) );
857
858 if( ra.m_existsAlready )
859 toPrune.insert( ra.m_area );
860
861 pruneExistingGroups( grpCommit, toPrune );
862
863 PCB_GROUP* grp = new PCB_GROUP( board() );
864
865 grpCommit.Add( grp );
866 grpCommit.Stage( ra.m_area, CHT_GROUP );
867
868 for( auto fp : ra.m_components )
869 {
870 grpCommit.Stage( fp, CHT_GROUP );
871 }
872 }
873 grpCommit.Push( _( "Group components with their placement rule areas" ) );
874 }
875
876
877 return true;
878}
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
virtual void Revert() override
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:342
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:475
constexpr BOX2< Vec > GetInflated(coord_type aDx, coord_type aDy) const
Get a new rectangle that is this one, inflated by aDx and aDy.
Definition: box2.h:638
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition: commit.h:74
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition: commit.h:92
virtual COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr)
Definition: commit.cpp:48
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:80
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Definition: commit.cpp:130
const std::vector< COMPONENT_CLASS * > & GetConstituentClasses() const
Fetches a vector of the constituent classes for this (effective) class.
int ShowModal() override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
const EDA_ANGLE & GetTextAngle() const
Definition: eda_text.h:130
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:204
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:2341
EDA_ANGLE GetOrientation() const
Definition: footprint.h:227
void SetOrientation(const EDA_ANGLE &aNewAngle)
Definition: footprint.cpp:2411
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:638
std::deque< PAD * > & Pads()
Definition: footprint.h:206
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:236
bool IsLocked() const override
Definition: footprint.h:411
void SetLayerAndFlip(PCB_LAYER_ID aLayer)
Used as Layer property setter – performs a flip if necessary to set the footprint layer.
Definition: footprint.cpp:2274
PCB_FIELD & Reference()
Definition: footprint.h:639
const wxString & GetReference() const
Definition: footprint.h:602
VECTOR2I GetPosition() const override
Definition: footprint.h:224
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)
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:60
int CheckRACompatibility(ZONE *aRefZone)
std::set< FOOTPRINT * > queryComponentsInSheet(wxString aSheetName) const
int findRoutedConnections(std::set< BOARD_ITEM * > &aOutput, std::shared_ptr< CONNECTIVITY_DATA > aConnectivity, const SHAPE_POLY_SET &aRAPoly, RULE_AREA *aRA, FOOTPRINT *aFp, const REPEAT_LAYOUT_OPTIONS &aOpts) const
int repeatLayout(const TOOL_EVENT &aEvent)
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)
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)
int RepeatLayout(const TOOL_EVENT &aEvent, ZONE *aRefZone)
int AutogenerateRuleAreas(const TOOL_EVENT &aEvent)
bool identifyComponentsInRuleArea(ZONE *aRuleArea, std::set< FOOTPRINT * > &aComponents)
wxString stripComponentIndex(wxString aRef) const
std::set< FOOTPRINT * > queryComponentsInComponentClass(const wxString &aComponentClassName) const
RULE_AREA * findRAByName(const wxString &aName)
bool copyRuleAreaContents(TMATCH::COMPONENT_MATCHES &aMatches, BOARD_COMMIT *aCommit, RULE_AREA *aRefArea, RULE_AREA *aTargetArea, REPEAT_LAYOUT_OPTIONS aOpts, std::unordered_set< BOARD_ITEM * > &aAffectedItems, std::unordered_set< BOARD_ITEM * > &aGroupableItems)
Definition: pad.h:54
void SetItems(BOARD_ITEM *a, BOARD_ITEM *b=nullptr)
static TOOL_ACTION repeatLayout
Definition: pcb_actions.h:592
static TOOL_ACTION generatePlacementRuleAreas
Definition: pcb_actions.h:593
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
virtual VECTOR2I GetPosition() const override
Definition: pcb_text.h:82
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:87
T * frame() const
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
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
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition: tool_base.h:146
Generic, UI-independent tool event.
Definition: tool_event.h:167
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).
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:721
wxString GetRuleAreaPlacementSource() const
Definition: zone.h:727
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition: zone.cpp:828
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:954
bool GetRuleAreaPlacementEnabled() const
Definition: zone.h:722
SHAPE_POLY_SET * Outline()
Definition: zone.h:337
const wxString & GetZoneName() const
Definition: zone.h:132
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:130
RULE_AREA_PLACEMENT_SOURCE_TYPE GetRuleAreaPlacementSourceType() const
Definition: zone.h:723
void UnHatchBorder()
Clear the zone's hatch.
Definition: zone.cpp:940
void RemoveAllContours(void)
Definition: zone.h:551
@ CHT_GROUP
Definition: commit.h:45
@ CHT_REMOVE
Definition: commit.h:43
@ CHT_UNGROUP
Definition: commit.h:46
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
Definition: convex_hull.cpp:87
#define _(s)
static const wxString traceMultichannelTool
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.
Definition: shape_utils.cpp:83
std::map< FOOTPRINT *, FOOTPRINT * > COMPONENT_MATCHES
Definition: topo_match.h:149
Class to handle a set of BOARD_ITEMs.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
see class PGM_BASE
Utility functions for working with shapes.
std::unordered_map< RULE_AREA *, RULE_AREA_COMPAT_DATA > m_compatMap
RULE_AREA * m_refRA
REPEAT_LAYOUT_OPTIONS m_options
std::vector< RULE_AREA > m_areas
TMATCH::COMPONENT_MATCHES m_matchingComponents
VECTOR2I m_center
RULE_AREA_PLACEMENT_SOURCE_TYPE m_sourceType
wxString m_sheetName
wxString m_componentClass
bool m_existsAlready
std::set< FOOTPRINT * > m_raFootprints
ZONE * m_oldArea
std::set< FOOTPRINT * > m_components
wxString m_ruleName
wxString m_sheetPath
bool m_generateEnabled
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96