71#include <wx/richmsgdlg.h>
75#define MULTICHANNEL_EXTRA_DEBUG
82 std::vector<wxString> refs;
89 refs.push_back( fp->GetReferenceAsString() );
92 std::sort( refs.begin(), refs.end(),
93 [](
const wxString& aLhs,
const wxString& aRhs )
95 return aLhs.CmpNoCase( aRhs ) < 0;
103 size_t componentsOnLine = 0;
105 for(
const wxString& ref : refs )
107 if( componentsOnLine == 10 )
114 componentsOnLine = 0;
121 if( !line.IsEmpty() )
137 for(
const wxString& reason : aReasons )
139 if( !
text.IsEmpty() )
150 const std::vector<wxString>& aReasons )
152 if( !aParent || aReasons.empty() )
155 wxRichMessageDialog dlg( aParent, aSummary,
_(
"Topology mismatch" ), wxICON_ERROR | wxOK );
166 aOut.insert(
static_cast<FOOTPRINT*
>( item ) );
180 else if( item->IsBOARD_ITEM() )
181 aOut.insert(
static_cast<BOARD_ITEM*
>( item ) );
254 const KIID& aAnchor )
256 auto it = std::ranges::find_if( aMatches, [&](
const auto& aPair ) {
return aPair.first->m_Uuid == aAnchor; } );
257 return it != aMatches.end() ? *it : *aMatches.begin();
268 if( aAnchor !=
niluuid && !aMatches.empty() )
270 const auto& [refAnchorFp, targetAnchorFp] =
anchorPair( aMatches, aAnchor );
271 VECTOR2I oldpos = refAnchorFp->GetPosition();
273 aRot = targetAnchorFp->GetOrientation() - refAnchorFp->GetOrientation();
275 aDisp = targetAnchorFp->GetPosition() - oldpos;
280 outline.
Move( aDisp );
288 std::set<ZONE*> zones;
295 ZONE* zone =
static_cast<ZONE*
>( aItem );
304 for(
EDA_ITEM* child : aGroup->GetItems() )
306 if(
ZONE* zone = asLinked( child ) )
308 zones.insert( zone );
319 if(
ZONE* zone = asLinked( item ) )
321 zones.insert( zone );
341 ZONE* zone =
static_cast<ZONE*
>( child );
391 const std::vector<wxString>& aReasons )
const
393 wxWindow* parent = aParent ? aParent :
frame();
401 wxCHECK( selTool,
false );
405 std::shared_ptr<MULTICHANNEL_CONTEXT_MENU> menu = std::make_shared<MULTICHANNEL_CONTEXT_MENU>(
this );
428 std::set<FOOTPRINT*>& aComponents )
430 if( !aRuleArea || !aRuleArea->
m_zone )
441 aComponents.insert(
static_cast<FOOTPRINT*
>( item ) );
444 return (
int) aComponents.size();
453 [&](
const wxString& aMessage,
int aOffset )
466 sourceName.Replace( wxT(
"'" ), wxT(
"\\'" ) );
473 ruleText = wxT(
"A.memberOfSheetOrChildren('" ) + sourceName + wxT(
"')" );
476 ruleText = wxT(
"A.hasComponentClass('" ) + sourceName + wxT(
"')" );
479 ruleText = wxT(
"A.memberOfGroup('" ) + sourceName + wxT(
"')" );
486 auto ok = compiler.
Compile( ruleText, &ucode, &preflightCtx );
500 fp->GetSheetname() );
502 aComponents.insert( fp );
512 if( !aRuleArea || !aRuleArea->
m_zone )
537 if( !boardItem->IsConnected() || boardItem->Type() ==
PCB_ZONE_T )
538 aItems.insert( boardItem );
550 && areaBox.
Contains( aItem->GetBoundingBox() );
555 if( !zone->GetIsRuleArea() && inside( zone ) )
556 aItems.insert( zone );
561 if( !drawing->IsConnected() && !drawing->GetParentGroup() && inside( drawing ) )
562 aItems.insert( drawing );
566 return aItems.size() > 0;
571 std::set<BOARD_ITEM*> members;
582 if( !boardItem->IsConnected() || boardItem->Type() ==
PCB_ZONE_T )
583 aItems.insert( boardItem );
594 bool zoneOnBoard =
false;
598 if( zone == aRuleArea->
m_zone )
614 std::set<BOARD_ITEM*> members;
623 if( !boardItem->IsConnected() || boardItem->Type() ==
PCB_ZONE_T )
624 aItems.insert( boardItem );
628 return aItems.size() > 0;
637 [&](
const wxString& aMessage,
int aOffset )
648 wxString ruleText = wxString::Format( wxT(
"A.enclosedByArea('%s')" ),
651 if( !compiler.
Compile( ruleText, &ucode, &preflightCtx ) )
658 auto val = ucode.
Run( &ctx );
660 if( val->AsDouble() != 0.0 )
661 aItems.insert( aItem );
666 if( zone == aRuleArea->
m_zone )
674 if( !drawing->IsConnected() )
675 testAndAdd( drawing );
684 std::set<FOOTPRINT*> rv;
686 if( aSheetName.EndsWith( wxT(
"/" ) ) )
687 aSheetName.RemoveLast();
689 wxString childPrefix = aSheetName + wxT(
"/" );
693 auto sn = fp->GetSheetname();
695 if( sn.EndsWith( wxT(
"/" ) ) )
698 if( sn == aSheetName || sn.StartsWith( childPrefix ) )
709 std::set<FOOTPRINT*> rv;
713 if( fp->GetComponentClass()->ContainsClassName( aComponentClassName ) )
723 std::set<FOOTPRINT*> rv;
727 if(
group->GetName().Matches( aGroupName ) )
741 if( !
group->GetName().Matches( aGroupName ) )
756 std::set<BOARD_ITEM*> rv;
760 if( !
group->GetName().Matches( aGroupName ) )
765 if( item->IsBOARD_ITEM() )
766 rv.insert(
static_cast<BOARD_ITEM*
>( item ) );
776 std::vector<VECTOR2I> bbCorners;
777 bbCorners.reserve( aFootprints.size() * 4 );
781 const BOX2I bb = fp->GetBoundingBox(
false ).GetInflated( aMargin );
785 std::vector<VECTOR2I> hullVertices;
796 std::vector<VECTOR2I> bbCorners;
797 bbCorners.reserve( aItems.size() * 4 );
801 BOX2I bb = item->GetBoundingBox();
804 bb =
static_cast<FOOTPRINT*
>( item )->GetBoundingBox(
false );
809 std::vector<VECTOR2I> hullVertices;
823 std::vector<wxString> segments;
826 for( wxUniChar ch : aSheetName )
831 segments.push_back( cur );
842 segments.push_back( cur );
844 std::vector<wxString> rv;
845 wxString prefix = wxT(
"/" );
847 for(
size_t i = 0; i + 1 < segments.size(); ++i )
849 prefix += segments[i] + wxT(
"/" );
850 rv.push_back( prefix );
861 std::map<wxString, wxString> uniqueSheets;
862 std::set<wxString> uniqueComponentClasses;
863 std::set<wxString> uniqueGroups;
869 uniqueSheets[fp->GetSheetname()] = fp->GetSheetfile();
873 uniqueSheets.emplace( parent, wxString() );
878 uniqueComponentClasses.insert( singleClass->GetName() );
884 if( !grp->GetName().IsEmpty() )
885 uniqueGroups.insert( grp->GetName() );
889 for(
const auto& [sheetPath, sheetFile] : uniqueSheets )
898 m_areas.m_areas.push_back( ent );
903 (
int)
m_areas.m_areas.size() );
906 for(
const wxString& compClass : uniqueComponentClasses )
914 m_areas.m_areas.push_back( ent );
918 static_cast<int>(
m_areas.m_areas.size() ) );
921 for(
const wxString& groupName : uniqueGroups )
929 m_areas.m_areas.push_back( ent );
933 static_cast<int>(
m_areas.m_areas.size() ) );
944 if( !zone->GetIsRuleArea() )
947 if( !zone->GetPlacementAreaEnabled() )
955 area.
m_center = zone->Outline()->COutline( 0 ).Centre();
973 m_areas.m_areas.push_back( area );
992 std::vector<ZONE*> refRAs;
994 auto isSelectedItemAnRA =
1000 ZONE* zone =
static_cast<ZONE*
>( aItem );
1013 if(
ZONE* zone = isSelectedItemAnRA( item ) )
1015 refRAs.push_back( zone );
1023 if(
ZONE* grpZone = isSelectedItemAnRA( grpItem ) )
1024 refRAs.push_back( grpZone );
1038 frame()->ShowInfoBarMsg(
_(
"Select the footprints of the circuit to repeat." ),
true );
1042 else if( refRAs.size() != 1 )
1047 _(
"Select a reference Rule Area to copy from..." ),
1050 return isSelectedItemAnRA( aItem ) !=
nullptr;
1065 if(
m_areas.m_areas.size() <= 1 )
1067 frame()->ShowInfoBarError(
_(
"No Rule Areas to repeat layout to have been found." ),
true );
1071 const KIID& linkSet = refRAs.front()->GetLinkSet();
1072 m_areas.m_options = refRAs.front()->GetRepeatOptions();
1076 for(
auto& [area, compat] :
m_areas.m_compatMap )
1077 compat.m_doCopy = compat.m_isOk && ( linkSet ==
niluuid || area->m_zone->GetLinkSet() == linkSet );
1094static bool isChannelNet(
const std::vector<FOOTPRINT*>& aOwners,
const std::set<FOOTPRINT*>& aChannel )
1096 return std::ranges::count_if( aOwners, [&](
FOOTPRINT* aFp ) {
return !aChannel.contains( aFp ); } ) <= 1;
1100static std::set<int>
channelNets(
const std::map<
int, std::vector<FOOTPRINT*>>& aNetOwners,
1101 const std::set<FOOTPRINT*>& aChannel )
1105 for(
const auto& [net, owners] : aNetOwners )
1107 if( owners.size() > 1 &&
isChannelNet( owners, aChannel )
1108 && std::ranges::any_of( owners, [&](
FOOTPRINT* aFp ) {
return aChannel.contains( aFp ); } ) )
1140 if( aAmong.contains(
pad->GetNetCode() ) )
1141 nets.insert(
pad->GetNetCode() );
1151 using PATH = std::vector<KIID>;
1153 std::optional<PATH> refPrefix;
1159 if(
path.size() < 2 )
1163 refPrefix = PATH(
path.begin(),
path.end() - 1 );
1165 size_t depth = std::min( refPrefix->size(),
path.size() - 1 );
1166 auto diff = std::mismatch( refPrefix->begin(), refPrefix->begin() + depth,
path.begin() );
1168 refPrefix->resize( diff.first - refPrefix->begin() );
1171 if( !refPrefix || refPrefix->empty() )
1174 const size_t depth = refPrefix->size();
1179 return PATH( aPath.begin() + depth, aPath.end() );
1182 std::map<PATH, FOOTPRINT*> refByRest;
1185 refByRest[rest( fp->GetPath() )] = fp;
1187 std::map<PATH, TMATCH::COMPONENT_MATCHES> instances;
1193 if( aRef.contains( fp ) ||
path.size() <= depth )
1196 PATH prefix(
path.begin(),
path.begin() + depth );
1198 if( prefix == *refPrefix )
1201 if(
auto it = refByRest.find( rest(
path ) ); it != refByRest.end() && it->second->GetFPID() == fp->GetFPID() )
1202 instances[prefix][it->second] = fp;
1205 std::vector<TMATCH::COMPONENT_MATCHES>
result;
1207 for(
auto& [prefix, matches] : instances )
1208 result.push_back( std::move( matches ) );
1235 int net =
pad->GetNetCode();
1249 if( it->second != peerNet )
1254 if( peerNet <= 0 ||
m_netsTaken.contains( peerNet ) )
1259 aAdded.push_back( net );
1270 std::vector<int> added;
1272 if(
bind( ref, aChoice, added ) )
1274 m_map[ref] = aChoice;
1275 m_used.insert( aChoice );
1277 if(
extend( aIndex + 1 ) )
1284 for(
int net : added )
1295 if( aIndex ==
m_order.size() )
1304 auto mapped = std::ranges::find_if( ref->
Pads(),
1305 [&](
PAD* aPad ) { return m_netMap.contains( aPad->GetNetCode() ); } );
1307 if( mapped != ref->
Pads().end() )
1312 &&
pair( aIndex, fp ) )
1333 m_areas.m_options.m_groupItems =
true;
1360 std::map<int, std::vector<FOOTPRINT*>> netOwners;
1364 for(
PAD*
pad : fp->Pads() )
1366 std::vector<FOOTPRINT*>& owners = netOwners[
pad->GetNetCode()];
1368 if(
pad->GetNetCode() > 0 && ( owners.empty() || owners.back() != fp ) )
1369 owners.push_back( fp );
1382 std::set<int> internal =
channelNets( netOwners, selected );
1383 std::set<FOOTPRINT*> leaves;
1387 if(
padNets( fp, internal ).size() <= 1 )
1388 leaves.insert( fp );
1391 std::vector<RULE_AREA_COMPAT_DATA> found;
1399 if( !aCandidate.m_discovered.contains( fp ) )
1400 aCandidate.m_unmatched.insert( fp );
1403 aCandidate.m_doCopy =
true;
1405 return aCandidate.m_unmatched.empty()
1406 || ( aLoose && std::ranges::all_of( aCandidate.m_unmatched,
1407 [&](
FOOTPRINT* aFp ) { return leaves.contains( aFp ); } ) );
1415 if( classify( candidate ) )
1416 found.push_back( std::move( candidate ) );
1421 std::map<LIB_ID, int> fpidCount;
1424 fpidCount[fp->GetFPID()]++;
1427 FOOTPRINT* seed = *std::ranges::min_element( selected,
1430 return std::make_pair( leaves.contains( aA ), fpidCount[aA->
GetFPID()] )
1431 < std::make_pair( leaves.contains( aB ), fpidCount[aB->
GetFPID()] );
1434 std::vector<FOOTPRINT*> order = { seed };
1436 for(
size_t i = 0; i < order.size(); ++i )
1438 for(
int net :
padNets( order[i], internal ) )
1442 if( selected.contains( fp ) && std::ranges::find( order, fp ) == order.end() )
1443 order.push_back( fp );
1448 std::set<FOOTPRINT*> claimed = selected;
1452 if( claimed.contains( twin ) || twin->GetFPID() != seed->
GetFPID() )
1455 FLAT_MATCHER matcher{ netOwners, internal, leaves, claimed, order, aLoose };
1457 bool ok = matcher.pair( 0, twin );
1459 if( matcher.m_budget < 0 )
1463 found.push_back( std::move( candidate ) );
1469 if( !ok || !classify( candidate ) )
1475 std::set<FOOTPRINT*> both;
1476 std::unordered_set<int> rails;
1478 for(
const auto& [ref, target] : candidate.
m_discovered )
1482 both.insert( { ref, target } );
1487 for(
PAD*
pad : fp->Pads() )
1490 rails.insert(
pad->GetNetCode() );
1501 found.push_back( std::move( candidate ) );
1511 const std::set<FOOTPRINT*>& selected =
m_areas.m_selectedFootprints;
1518 m_areas.m_scratchItems.clear();
1520 if( selected.empty() )
1523 std::vector<RULE_AREA_COMPAT_DATA> found =
findMatches( selected, aLoose );
1526 m_areas.m_areas.reserve( found.size() + 1 );
1529 [&](
const SHAPE_LINE_CHAIN& aOutline,
const std::set<FOOTPRINT*>& aFootprints,
1535 m_areas.m_scratchItems.emplace_back( zone );
1554 std::set<FOOTPRINT*> targets;
1556 for(
const auto& [ref, target] : candidate.m_discovered )
1557 targets.insert( target );
1564 for(
size_t i = 0; i < found.size(); ++i )
1565 m_areas.m_compatMap[&
m_areas.m_areas[i + 1]] = std::move( found[i] );
1569 bool unticked =
false;
1571 for(
auto& [area, compat] :
m_areas.m_compatMap )
1573 unticked |= compat.m_doCopy && !compat.m_isOk;
1574 compat.m_doCopy &= compat.m_isOk;
1578 if( unticked && resolved )
1582 (
int)
m_areas.m_compatMap.size() );
1592 [&](
const std::set<FOOTPRINT*>& aSet )
1596 return std::ranges::any_of(
findMatches( aSet,
false ),
1603 std::set<FOOTPRINT*>
result = aSeed;
1611 if( aLargest &&
path.size() >= 2 )
1613 std::set<FOOTPRINT*> sheet;
1619 if( other.size() ==
path.size() && std::equal(
path.begin(),
path.end() - 1, other.begin() ) )
1623 bool holdsSeed = std::ranges::all_of( aSeed, [&](
FOOTPRINT* aFp ) {
return sheet.contains( aFp ); } );
1625 if( sheet.size() >
result.size() && holdsSeed && hasTwin( sheet ) )
1634 constexpr size_t maxOwners = 12;
1636 std::set<FOOTPRINT*> rejected;
1639 while( grew && budget > 0 )
1644 const std::set<FOOTPRINT*> frontier =
result;
1646 for(
const auto& [net, owners] : netOwners )
1648 if( owners.size() > maxOwners || !std::ranges::any_of( owners, [&](
FOOTPRINT* aFp )
1649 { return frontier.contains( aFp ); } ) )
1659 if(
result.contains( fp ) || rejected.contains( fp ) )
1662 std::set<FOOTPRINT*> trial =
result;
1665 if( hasTwin( trial ) )
1667 result = std::move( trial );
1672 rejected.insert( fp );
1703 std::set<FOOTPRINT*> seed;
1708 seed.insert(
static_cast<FOOTPRINT*
>( item ) );
1715 frame()->ShowInfoBarMsg(
_(
"Select a footprint to expand from." ),
true );
1721 if( expanded.size() == seed.size() )
1723 frame()->ShowInfoBarMsg(
_(
"No larger topology repeats elsewhere on the board." ),
true );
1727 EDA_ITEMS items( expanded.begin(), expanded.end() );
1732 frame()->ShowInfoBarMsg( wxString::Format(
_(
"Selected %d parts that repeat elsewhere." ), (
int) expanded.size() ),
1741 std::set<FOOTPRINT*> pruned;
1747 for(
const auto& [area, compat] :
m_areas.m_compatMap )
1749 if( compat.m_doCopy )
1750 pruned.insert( compat.m_unmatched.begin(), compat.m_unmatched.end() );
1769 for(
auto& [area, compat] :
m_areas.m_compatMap )
1771 compat.m_isOk =
false;
1772 compat.m_errorMsg =
_(
"The ticked channels share no selected part" );
1780 [&](
FOOTPRINT* aFp ) { return aFp->m_Uuid == m_areas.m_options.m_anchor; } ) )
1783 int64_t biggest = -1;
1795 std::vector<TOPOLOGY_JOB> jobs;
1797 for(
auto& [area, compat] :
m_areas.m_compatMap )
1799 area->m_components.clear();
1803 if(
auto it = compat.m_discovered.find( fp ); it != compat.m_discovered.end() )
1804 area->m_components.insert( it->second );
1807 if( compat.m_searchLimit )
1809 compat.m_isOk =
false;
1810 compat.m_errorMsg =
_(
"Search limit reached" );
1814 jobs.push_back( { ref, area, &compat } );
1846 std::vector<RULE_AREA*> channels = { ref };
1849 for(
auto& [area, compat] :
m_areas.m_compatMap )
1851 if( compat.m_doCopy && compat.m_isOk )
1852 channels.push_back( area );
1855 if( channels.size() < 2 )
1861 auto it = std::ranges::find_if(
m_areas.m_scratchItems,
1862 [&](
const auto& aScratch ) { return aScratch.get() == aItem; } );
1863 commit.
Add( it->release() );
1866 std::set<wxString> names;
1869 names.insert(
group->GetName() );
1874 RULE_AREA* matched = area == ref ? channels[1] : area;
1875 const auto& [refAnchor, targetAnchor] =
1880 [&](
const wxString& aName ) { return names.contains( aName ); } );
1881 names.insert(
name );
1883 area->m_group->SetName(
name );
1884 area->m_zone->SetZoneName(
name );
1887 wxString sheet =
anchor->GetSheetname();
1892 area->m_zone->SetPlacementAreaSource( wholeSheet ? sheet : area->m_group->GetName() );
1894 addToBoard( area->m_zone );
1895 addToBoard( area->m_group );
1898 for(
FOOTPRINT* fp : area->m_components )
1909 std::set<BOARD_ITEM*> found;
1911 if(
m_areas.m_options.m_copyRouting )
1918 nets.insert(
pad->GetNetCode() );
1922 found.insert( item );
1925 if(
m_areas.m_options.m_copyOtherItems )
1936 std::vector<RULE_AREA*> targets;
1941 targets.push_back( area );
1951 int created = (int) channels.size();
1953 commit.
Push(
_(
"Repeat Layout" ) );
1961 frame()->ShowInfoBarMsg( wxString::Format(
m_areas.m_linkAreas ?
_(
"Created %d linked channels." )
1962 :
_(
"Created %d channels." ),
1976 refs.push_back( fp->GetReference() );
1979 return wxJoin( refs,
',' );
2015 auto savedCompat =
m_areas.m_compatMap;
2020 m_areas.m_options.m_groupItems =
false;
2030 view->AddToPreview( clone,
true );
2044 if( isContainer( aItem ) )
2046 aItem->RunOnChildren( [&](
BOARD_ITEM* aChild )
2048 if( !isContainer( aChild ) )
2049 previewClone( aChild );
2061 for(
const auto& [area, compat] :
m_areas.m_compatMap )
2063 for(
BOARD_ITEM* item : compat.m_affectedItems )
2066 for(
BOARD_ITEM* item : compat.m_groupableItems )
2071 for(
const auto& [area, compat] :
m_areas.m_compatMap )
2073 for(
BOARD_ITEM* item : compat.m_affectedItems )
2087 std::vector<BOARD_ITEM*>
moved;
2090 moved.push_back( fp );
2093 connectivity->Update( fp );
2095 connectivity->RecalculateRatsnest();
2098 [&](
const std::shared_ptr<const CN_ANCHOR>& aNode )
2106 for(
const CN_EDGE& edge : connectivity->GetRatsnestForItems(
moved ) )
2108 const std::shared_ptr<const CN_ANCHOR>& a = edge.GetSourceNode();
2109 const std::shared_ptr<const CN_ANCHOR>& b = edge.GetTargetNode();
2111 if( a && b && !a->Dirty() && !b->Dirty() && ( movedPad( a ) || movedPad( b ) ) )
2120 m_areas.m_compatMap = std::move( savedCompat );
2121 m_areas.m_options = savedOptions;
2134 std::vector<BOARD_ITEM*>
moved;
2137 moved.push_back( fp );
2148 std::set<BOARD_CONNECTED_ITEM*> scanned;
2149 std::set<BOARD_CONNECTED_ITEM*> routing;
2155 if( aNets.contains( item->GetNetCode() ) )
2156 routing.insert( item );
2163 const std::set<BOARD_CONNECTED_ITEM*> inside = routing;
2170 for(
CN_ITEM* cnItem : connectivity->GetConnectivityAlgo()->ItemEntry( item ).GetItems() )
2185 if(
next->GetNetCode() == item->GetNetCode() && aNets.contains(
next->GetNetCode() ) )
2186 routing.insert(
next );
2214 m_highlight->SetStrokeColor( aColor.WithAlpha( 0.9 ) );
2215 m_highlight->SetFillColor( aColor.WithAlpha( 0.08 ) );
2252 view->ClearPreview();
2284 if( !item || !
view->HasItem( item ) )
2287 view->Hide( item, aHide );
2319 if( ra.
m_zone == aRefZone )
2331 std::vector<TOPOLOGY_JOB> jobs;
2338 jobs.push_back( {
m_areas.m_refRA, &ra, &
m_areas.m_compatMap[&ra] } );
2356 int total =
static_cast<int>( aJobs.size() );
2357 std::atomic<int> completed( 0 );
2358 std::atomic<bool> cancelled(
false );
2359 std::atomic<int> matchedComponents( 0 );
2360 std::atomic<int> totalComponents( 0 );
2373 auto future = std::async(
2375 [
this, &aJobs, &completed, &cancelled, &matchedComponents, &isoParams]()
2379 if( cancelled.load( std::memory_order_relaxed ) )
2382 matchedComponents.store( 0, std::memory_order_relaxed );
2385 if( cancelled.load( std::memory_order_relaxed ) )
2388 completed.fetch_add( 1, std::memory_order_relaxed );
2396 std::unique_ptr<WINDOW_DISABLER> dialogDisabler;
2397 std::unique_ptr<WINDOW_DISABLER> frameDisabler;
2398 std::unique_ptr<WX_PROGRESS_REPORTER>
reporter;
2399 auto startTime = std::chrono::steady_clock::now();
2400 double highWaterMark = 0.0;
2402 while( future.wait_for( std::chrono::milliseconds( 100 ) ) != std::future_status::ready )
2407 if( std::chrono::steady_clock::now() - startTime > std::chrono::milliseconds( 250 ) )
2411 dialogDisabler = std::make_unique<WINDOW_DISABLER>(
m_dialog );
2412 frameDisabler = std::make_unique<WINDOW_DISABLER>(
frame() );
2418 wxLog::FlushActive();
2424 int done = completed.load( std::memory_order_relaxed );
2425 int matched = matchedComponents.load( std::memory_order_relaxed );
2426 int compTotal = totalComponents.load( std::memory_order_relaxed );
2428 double fraction = ( compTotal > 0 )
2429 ?
static_cast<double>( matched ) / compTotal
2431 double progress =
static_cast<double>( done + fraction ) / total;
2433 if( progress > highWaterMark )
2434 highWaterMark = progress;
2436 reporter->SetCurrentProgress( highWaterMark );
2437 reporter->Report( wxString::Format(
2438 _(
"Resolving topology %d of %d (%d/%d components)" ),
2439 std::min( done + 1, total ), total, matched, compTotal ) );
2442 cancelled.store(
true, std::memory_order_relaxed );
2451 if( !cancelled.load( std::memory_order_relaxed ) )
2455 for(
size_t i =
static_cast<size_t>( completed.load() ); i < aJobs.size(); ++i )
2457 aJobs[i].m_compat->m_isOk =
false;
2458 aJobs[i].m_compat->m_errorMsg =
_(
"Cancelled" );
2459 aJobs[i].m_compat->m_mismatchReasons.clear();
2468 wxString* aErrorOut )
2470 wxCHECK_MSG( aRefArea.
m_zone, -1, wxT(
"Reference Rule Area has no zone." ) );
2471 wxCHECK_MSG( aTargetArea.
m_zone, -1, wxT(
"Target Rule Area has no zone." ) );
2473 const bool silent = aErrorOut !=
nullptr;
2475 auto reportError = [&](
const wxString& aMsg )
2479 else if(
Pgm().IsGUI() )
2480 frame()->ShowInfoBarError( aMsg,
true );
2492 else if(
Pgm().IsGUI() )
2494 wxString summary = wxString::Format(
_(
"Rule Area topologies do not match: %s" ), compat.
m_errorMsg );
2501 std::optional<BOARD_COMMIT> localCommit;
2503 if( !aExternalCommit )
2504 localCommit.emplace(
GetManager(),
true,
false );
2506 BOARD_COMMIT& commit = aExternalCommit ? *aExternalCommit : *localCommit;
2529 if( !aExternalCommit )
2532 reportError(
_(
"Target group does not have a group." ) );
2542 if(
group->ContainsItem( item ) )
2546 group->AddItem( item );
2550 if( !aExternalCommit )
2551 commit.
Push(
_(
"Repeat Layout" ) );
2559 int totalCopied = 0;
2561 for(
auto& [targetArea, compatData] :
m_areas.m_compatMap )
2563 if( !compatData.m_doCopy )
2566 targetArea->m_ruleName );
2570 if( !compatData.m_isOk )
2577 if(
m_areas.m_options.m_groupItems )
2579 for(
const auto& [targetArea, compatData] :
m_areas.m_compatMap )
2581 if( compatData.m_groupableItems.size() < 2 )
2600 for(
BOARD_ITEM* item : compatData.m_groupableItems )
2616 std::vector<RULE_AREA*> targets;
2618 for(
const auto& [targetArea, compatData] :
m_areas.m_compatMap )
2620 if( compatData.m_doCopy && compatData.m_isOk )
2621 targets.push_back( targetArea );
2627 commit.
Push(
_(
"Repeat Layout" ) );
2630 frame()->ShowInfoBarMsg( wxString::Format(
_(
"Copied to %d Rule Areas." ), totalCopied ),
true );
2650 std::set<KIID> oldSets;
2653 auto join = [&](
ZONE* aZone,
const KIID& aAnchor )
2679 join( target->m_zone,
anchor );
2689 std::map<KIID, std::vector<ZONE*>> members;
2693 if( aSets.contains( zone->GetLinkSet() ) )
2694 members[zone->GetLinkSet()].push_back( zone );
2697 for(
const auto& [linkSet, zones] : members )
2699 if( zones.size() < 2 )
2712 || ( generators && generators->Router() && generators->Router()->RoutingInProgress() );
2720 if( sources.size() != 1 )
2722 frame()->ShowInfoBarMsg( sources.empty() ?
_(
"Select a linked Rule Area, or a part of one, to update the "
2724 :
_(
"Select items from a single linked channel." ),
2731 frame()->ShowInfoBarWarning(
_(
"Finish routing before updating linked layouts." ),
true );
2744 if( members.empty() )
2745 frame()->ShowInfoBarMsg(
_(
"Select a linked Rule Area, or a part of one, to unlink." ),
true );
2756 std::set<KIID> oldSets;
2758 for(
ZONE* zone : aZones )
2760 if( zone->GetLinkSet() !=
niluuid )
2761 oldSets.insert( zone->GetLinkSet() );
2767 commit.
Push(
_(
"Unlink Layout" ) );
2779 std::vector<RULE_AREA*> linked;
2780 std::map<FOOTPRINT*, int> footprintUse;
2787 linked.push_back( &area );
2789 if( area.
m_zone == aSource )
2803 return std::ranges::any_of( aArea->m_components,
2804 [&](
FOOTPRINT* aFp ) { return footprintUse[aFp] > 1; } );
2808 auto overlapsOtherArea =
2811 return std::ranges::any_of( linked,
2814 return aOther != aArea
2822 if( sharesParts( ref ) || ( !refGrouped && overlapsOtherArea( ref, *ref->
m_zone->
Outline() ) ) )
2826 frame()->ShowInfoBarWarning(
2827 wxString::Format(
_(
"'%s' overlaps another linked Rule Area." ), ref->
m_ruleName ),
true );
2834 std::vector<wxString> problems;
2835 std::vector<SHAPE_POLY_SET> acceptedOutlines;
2841 std::vector<RULE_AREA*> targets;
2842 std::vector<TOPOLOGY_JOB> jobs;
2849 targets.push_back( target );
2850 jobs.push_back( { ref, target, &
m_areas.m_compatMap[target] } );
2865 problems.push_back( wxString::Format( wxT(
"'%s': %s" ), target->m_ruleName, compat.
m_errorMsg ) );
2876 bool overlapsAccepted = std::ranges::any_of( acceptedOutlines,
2884 if( sharesParts( target )
2885 || ( !scoped && ( overlapsAccepted || overlapsOtherArea( target, *target->m_zone->Outline() )
2886 || overlapsOtherArea( target, repeated ) ) ) )
2889 wxString::Format(
_(
"'%s' overlaps another linked Rule Area." ), target->m_ruleName ) );
2894 acceptedOutlines.push_back( std::move( repeated ) );
2899 if( !problems.empty() &&
Pgm().IsGUI() )
2901 frame()->ShowInfoBarWarning(
2902 wxString::Format(
_(
"Some linked Rule Areas were not updated. %s" ),
JoinMismatchReasons( problems ) ),
2916 if( !aRuleArea || !aRuleArea->
m_zone )
2945 if(
EDA_GROUP* parent = bci->GetParentGroup() )
2951 if( bci->IsConnected() )
2952 aOutput.insert( bci );
2957 return (
int) aOutput.size();
2959 count = (int) aOutput.size();
2964 std::set<BOARD_ITEM*> members;
2980 aOutput.insert( bci );
2991 bool zoneOnBoard =
false;
2995 if( zone == aRuleArea->
m_zone )
3005 const BOX2I areaBox = areaOutline.
BBox();
3010 if( aOutput.contains( aItem ) )
3014 if(
EDA_GROUP* parent = aItem->GetParentGroup() )
3021 || !areaBox.
Contains( aItem->GetBoundingBox() ) )
3027 aItem->TransformShapeToPolygon( itemShape, aItem->GetLayer(), 0, maxError,
ERROR_OUTSIDE );
3036 aOutput.insert( aItem );
3042 enclosedByZone( track );
3046 if( drawing->IsConnected() )
3059 [&](
const wxString& aMessage,
int aOffset )
3070 wxString ruleText = wxString::Format( wxT(
"A.enclosedByArea('%s')" ),
3078 if( aOutput.contains( aItem ) )
3082 if(
EDA_GROUP* parent = aItem->GetParentGroup() )
3090 || !zoneBox.
Intersects( aItem->GetBoundingBox() ) )
3100 aOutput.insert( aItem );
3105 if( compiler.
Compile( ruleText, &ucode, &preflightCtx ) )
3108 testAndAdd( track );
3112 if( drawing->IsConnected() )
3131 std::map<int, int> netMap;
3135 for(
const PAD* refPad : refFP->Pads() )
3137 if( refPad->GetNetCode() <= 0 )
3140 std::vector<const PAD*> targetPads = targetFP->GetPads( refPad->GetNumber() );
3142 if( !targetPads.empty() )
3143 netMap.emplace( refPad->GetNetCode(), targetPads[0]->GetNetCode() );
3162 std::vector<std::pair<BOARD_ITEM*, BOARD_ITEM*>> groupFixupPairs;
3163 std::set<BOARD_ITEM*> reproducedSourceItems;
3165 auto fixupParentGroup =
3170 destItem->SetParentGroup(
nullptr );
3172 if( !preserveGroups )
3176 groupFixupPairs.emplace_back( sourceItem, destItem );
3178 reproducedSourceItems.insert( sourceItem );
3184 bool targetZoneOnBoard =
false;
3186 if( aTargetArea->
m_zone )
3190 if( z == aTargetArea->
m_zone )
3192 targetZoneOnBoard =
true;
3198 if( targetZoneOnBoard )
3206 if( preserveGroups )
3209 groupFixupPairs.emplace_back( aRefArea->
m_zone, aTargetArea->
m_zone );
3211 reproducedSourceItems.insert( aRefArea->
m_zone );
3217 std::set<BOARD_CONNECTED_ITEM*> refRouting;
3218 std::set<BOARD_CONNECTED_ITEM*> targetRouting;
3224 std::set<int> targc;
3228 for(
PAD*
pad : refFP->Pads() )
3229 refc.insert(
pad->GetNetCode() );
3231 for(
PAD*
pad : targetFP->Pads() )
3232 targc.insert(
pad->GetNetCode() );
3249 std::set<int> targetGroupNets;
3254 targetGroupNets.insert(
pad->GetNetCode() );
3259 std::set<BOARD_ITEM*> members;
3265 targetGroupNets.insert( bci->GetNetCode() );
3272 std::set<int> targetChannelNets;
3274 for(
int net : targetGroupNets )
3278 targetChannelNets.insert( net );
3282 std::vector<FOOTPRINT*> owners;
3286 if( std::ranges::find( owners,
pad->GetParentFootprint() ) == owners.end() )
3287 owners.push_back(
pad->GetParentFootprint() );
3291 targetChannelNets.insert( net );
3308 if( item->GetParentGroup() )
3311 if( item->IsLocked() )
3314 if( item->GetNetCode() <= 0 || !targetChannelNets.contains( item->GetNetCode() ) )
3358 fixupParentGroup( item,
copied );
3368 std::vector<PCB_GENERATOR*> refGenerators;
3369 std::vector<PCB_GENERATOR*> targetGenerators;
3371 auto appendGenerators =
3372 [&](
EDA_GROUP* aGroup, std::vector<PCB_GENERATOR*>& aOut )
3374 std::set<BOARD_ITEM*> members;
3389 refGenerators.push_back(
static_cast<PCB_GENERATOR*
>( item ) );
3394 if( !targetGroup && !aTargetArea->
m_components.empty() )
3395 targetGroup = ( *aTargetArea->
m_components.begin() )->GetParentGroup();
3398 appendGenerators( targetGroup, targetGenerators );
3407 appendGenerators( refGroup, refGenerators );
3410 appendGenerators( targetGroup, targetGenerators );
3412 if( !refGroup || !targetGroup )
3419 if( gen->GetGeneratorType() != wxT(
"tuning_pattern" ) )
3422 if( !refGroup && gen->HitTest( refOut,
false ) )
3423 refGenerators.push_back( gen );
3424 else if( !targetGroup && !( refGroup && gen->HitTest( refOut,
false ) )
3425 && gen->HitTest( targetOut,
false ) )
3426 targetGenerators.push_back( gen );
3440 aCommit->
Remove( child );
3464 clone->
Move( disp );
3465 aCommit->
Add( clone );
3473 fixupNet( bci, bci, hasMatches, netMap );
3475 aCommit->
Add( child );
3485 std::set<BOARD_ITEM*> sourceItems;
3486 std::set<BOARD_ITEM*> targetItems;
3504 if( item->GetParent() && item->GetParent()->Type() ==
PCB_FOOTPRINT_T )
3521 ZONE* zone =
static_cast<ZONE*
>( item );
3544 if( item->GetParent() && item->GetParent()->Type() ==
PCB_FOOTPRINT_T )
3554 ZONE* zone =
static_cast<ZONE*
>( item );
3556 if( !skipLayerFilter )
3558 LSET allowedLayers =
3566 ZONE* targetZone =
static_cast<ZONE*
>( item->Duplicate(
false ) );
3567 fixupNet( zone, targetZone, hasMatches, netMap );
3573 if( !skipLayerFilter )
3587 fixupParentGroup( item,
copied );
3605 refFP->GetReference() );
3611 refFP->GetReference() );
3619 && !refFP->GetEffectiveShape( refFP->GetLayer() )->Collide( &refPoly, 0 ) )
3627 aCommit->
Modify( targetFP );
3629 targetFP->SetLayerAndFlip( refFP->GetLayer() );
3630 targetFP->SetOrientation( refFP->GetOrientation() );
3631 targetFP->SetPosition( refFP->GetPosition() );
3632 targetFP->Rotate(
VECTOR2( 0, 0 ), rot );
3633 targetFP->Move( disp );
3635 for(
PCB_FIELD* refField : refFP->GetFields() )
3637 wxCHECK2( refField,
continue );
3639 PCB_FIELD* targetField = targetFP->GetField( refField->GetName() );
3644 targetField->
SetLayerSet( refField->GetLayerSet() );
3645 targetField->
SetVisible( refField->IsVisible() );
3647 targetField->
SetPosition( refField->GetPosition() );
3650 targetField->
Move( disp );
3657 std::set<PCB_TEXT*> consumedTargets;
3659 for(
BOARD_ITEM* refItem : refFP->GraphicalItems() )
3667 for(
BOARD_ITEM* targetItem : targetFP->GraphicalItems() )
3674 if( consumedTargets.contains( candidate ) || candidate->
GetText() != refText->
GetText() )
3679 targetText = candidate;
3688 consumedTargets.insert( targetText );
3696 targetText->
Move( disp );
3701 targetFP->Models() = refFP->Models();
3703 std::set<PAD*> consumedPads;
3705 for(
PAD* refPad : refFP->Pads() )
3707 for(
PAD* targetPad : targetFP->Pads() )
3709 if( consumedPads.contains( targetPad ) || targetPad->GetNumber() != refPad->GetNumber() )
3714 consumedPads.insert( targetPad );
3715 targetPad->ImportSettingsFrom( *refPad );
3725 if( preserveGroups && refFP->GetParentGroup() )
3726 groupFixupPairs.emplace_back( refFP, targetFP );
3728 if( preserveGroups )
3729 reproducedSourceItems.insert( refFP );
3736 if( preserveGroups && !groupFixupPairs.empty() )
3738 std::map<EDA_GROUP*, EDA_GROUP*> groupMap;
3739 std::map<EDA_GROUP*, bool> fullyReproducedCache;
3741 auto groupFullyReproduced =
3744 if(
auto it = fullyReproducedCache.find( aGroup ); it != fullyReproducedCache.end() )
3747 bool reproduced =
true;
3749 for(
EDA_ITEM* member : aGroup->GetItems() )
3753 if( !member->IsBOARD_ITEM()
3754 || !reproducedSourceItems.contains(
static_cast<BOARD_ITEM*
>( member ) ) )
3761 fullyReproducedCache[aGroup] = reproduced;
3765 for(
const auto& [sourceItem, destItem] : groupFixupPairs )
3769 if( !parentGroup || !groupFullyReproduced( parentGroup ) )
3772 if( !groupMap.contains( parentGroup ) )
3782 newGroup->
Move( disp );
3785 groupMap[parentGroup] = newGroup;
3786 aCommit->
Add( newGroup );
3791 if(
EDA_GROUP* oldGroup = destItem->GetParentGroup() )
3793 if( oldGroup != groupMap[parentGroup] )
3794 aCommit->
Modify( oldGroup->AsEdaItem() );
3797 groupMap[parentGroup]->AddItem( destItem );
3813 const std::map<int, int>& aNetMap )
3822 auto it = aNetMap.find( aRef->
GetNetCode() );
3824 if( it != aNetMap.end() )
3830 const std::set<FOOTPRINT*>& aFootprints,
3831 const std::unordered_set<EDA_ITEM*>& aItems )
3833 std::vector<NETINFO_ITEM*> created;
3834 std::map<int, NETINFO_ITEM*> remap;
3840 auto isAutoName = [](
const wxString& aName )
3842 return aName.StartsWith( wxT(
"Net-(" ) ) || aName.StartsWith( wxT(
"unconnected-" ) );
3847 int code = aItem->GetNetCode();
3854 if( !oldNet || !isAutoName( oldNet->
GetNetname() ) )
3857 auto it = remap.find( code );
3859 if( it == remap.end() )
3865 name = wxString::Format( wxT(
"__dbapply_%d_%d" ), code, counter++ );
3869 aBoard->
Add( newNet );
3870 created.push_back( newNet );
3871 it = remap.emplace( code, newNet ).first;
3874 aItem->SetNet( it->second );
3879 for(
PAD*
pad : fp->Pads() )
3898 if( aRef.empty() || aRef.size() != aTarget.size() )
3902 std::set<FOOTPRINT*> used;
3907 size_t bestLength = 0;
3914 if( length > bestLength )
3916 targetFp = candidate;
3917 bestLength = length;
3920 else if( length == bestLength && length > 0 )
3927 if( !targetFp || tied )
3931 if( refFp->GetFPID() != targetFp->
GetFPID() || refFp->Pads().size() != targetFp->
Pads().size() )
3934 if( !used.insert( targetFp ).second )
3937 result[refFp] = targetFp;
3940 aResult = std::move(
result );
3948 const std::unordered_set<int>& aExtraGlobalNets )
3968 std::set<FOOTPRINT*> shared;
3971 std::inserter( shared, shared.begin() ) );
3973 if( !shared.empty() )
3977 aMatches.
m_errorMsg =
_(
"Target Rule Area shares components with the reference area" );
3980 _(
"This target Rule Area selects the same components as the reference area. "
3981 "Repeat layout cannot copy a Rule Area onto itself. Give each placement Rule "
3982 "Area a distinct sheet, component class or group." ) );
3983 aMatches.
m_mismatchReasons.push_back( wxString::Format(
_(
"Shared components:\n%s" ),
3991 std::unordered_map<int, std::vector<const RULE_AREA*>> netInternalAreas;
3995 std::unordered_map<int, int> areaNetPadCounts;
4001 if(
pad->GetNetCode() > 0 )
4002 areaNetPadCounts[
pad->GetNetCode()]++;
4006 for(
const auto& [netCode, padCount] : areaNetPadCounts )
4009 netInternalAreas[netCode].push_back( &area );
4020 if( aB->m_components.count( fp ) )
4027 std::unordered_set<int> globalNets = aExtraGlobalNets;
4029 for(
const auto& [netCode, areas] : netInternalAreas )
4031 for(
size_t i = 0; i < areas.size() && !globalNets.count( netCode ); i++ )
4033 for(
size_t j = i + 1; j < areas.size(); j++ )
4035 if( disjoint( areas[i], areas[j] ) )
4037 globalNets.insert( netCode );
4058 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> mismatchReasons;
4062 mismatchReasons, aParams );
4074 if( !status && !cancelled
4081 aMatches.
m_isOk = status;
4092 for(
const auto& reason : mismatchReasons )
4094 if( reason.m_reason.IsEmpty() )
4097 if( !reason.m_reference.IsEmpty() && !reason.m_candidate.IsEmpty() )
4099 else if( !reason.m_reference.IsEmpty() )
4103 reason.m_reason ) );
4110 aMatches.
m_mismatchReasons.push_back(
_(
"The components in the two areas could not be paired up." ) );
4117 aMatches.
m_mismatchReasons.push_back( wxString::Format(
_(
"Reference area components:\n%s" ),
4119 aMatches.
m_mismatchReasons.push_back( wxString::Format(
_(
"Target area components:\n%s" ),
4130 const std::unordered_set<BOARD_ITEM*>& aItemsToRemove )
4136 std::vector<EDA_ITEM*> pruneList;
4142 if( refItem->m_Uuid == testItem->m_Uuid )
4143 pruneList.push_back( refItem );
4147 if( !pruneList.empty() )
4152 group->RemoveItem( item );
4154 if(
group->GetItems().size() < 2 )
4171 if(
m_areas.m_areas.size() <= 1 )
4173 frame()->ShowInfoBarError(
_(
"Cannot auto-generate any placement areas because the "
4174 "schematic has only one or no hierarchical sheets, "
4175 "groups, or component classes." ),
4183 if( ret != wxID_OK )
4203 wxString existingAreaTraceFormat;
4208 existingAreaTraceFormat = wxT(
"Placement rule area for sheet '%s' already exists as '%s'\n" );
4213 existingAreaTraceFormat = wxT(
"Placement rule area for component class '%s' already exists as '%s'\n" );
4218 existingAreaTraceFormat = wxT(
"Placement rule area for group '%s' already exists as '%s'\n" );
4221 std::unique_ptr<ZONE> newZone(
new ZONE(
board() ) );
4222 newZone->SetZoneName( wxString::Format( wxT(
"auto-placement-area-%s" ),
source ) );
4223 newZone->SetPlacementAreaSourceType( ra.
m_sourceType );
4224 newZone->SetPlacementAreaSource(
source );
4226 std::vector<ZONE*> oldZones;
4230 if( !zone->GetIsRuleArea() || !zone->GetPlacementAreaEnabled() )
4233 if( zone->GetPlacementAreaSourceType() != newZone->GetPlacementAreaSourceType()
4234 || zone->GetPlacementAreaSource() != newZone->GetPlacementAreaSource() )
4241 oldZones.push_back( zone );
4244 if( !oldZones.empty() && !
m_areas.m_replaceExisting )
4256 if( groupItems.empty() )
4259 wxT(
"Skipping placement rule area generation for source group '%s': group has no board items." ),
4271 std::set<BOARD_ITEM*> outlineItems;
4272 std::set<EDA_GROUP*> groups;
4277 outlineItems.insert( fp );
4291 std::set<int> foreignNets;
4299 foreignNets.insert(
pad->GetNetCode() );
4304 int net = track->GetNetCode();
4306 if( net > 0 &&
channelNets.count( net ) && !foreignNets.count( net ) )
4307 outlineItems.insert( track );
4314 newZone->GetZoneName(),
4319 for(
ZONE* oldZone : oldZones )
4320 commit.
Remove( oldZone );
4322 ra.
m_zone = newZone.release();
4329 if(
m_areas.m_options.m_groupItems )
4336 std::unordered_set<BOARD_ITEM*> toPrune;
4352 group->AddItem( fp );
4357 commit.
Push(
_(
"Auto-generate Placement Rule Areas" ) );
constexpr EDA_IU_SCALE pcbIUScale
#define SKIP_ENTERED_GROUP
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
static TOOL_ACTION selectionClear
Clear the current selection.
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
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,...
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
bool IsConnected() const override
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual void SetLayerSet(const LSET &aLayers)
virtual bool IsKnockout() const
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual void SetIsKnockout(bool aKnockout)
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Information pertinent to a Pcbnew printed circuit board.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
const FOOTPRINTS & Footprints() const
static wxString MakeUniqueZoneName(const wxString &aBaseName, const std::function< bool(const wxString &)> &aInUse)
Return a name based on aBaseName for which aInUse returns false, adding or incrementing a _<number> s...
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
BOARD_DESIGN_SETTINGS & GetDesignSettings()
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
constexpr const Vec GetEnd() const
constexpr ecoord_type GetArea() const
Return the area of the rectangle.
constexpr bool Contains(const Vec &aPoint) const
constexpr BOX2< Vec > GetInflated(coord_type aDx, coord_type aDy) const
Get a new rectangle that is this one, inflated by aDx and aDy.
constexpr const Vec & GetOrigin() const
constexpr bool Intersects(const BOX2< Vec > &aRect) const
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
CN_ITEM represents a BOARD_CONNECTED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
const std::vector< CN_ITEM * > & ConnectedItems() const
Represent a set of changes (additions, deletions or modifications) of a data model (e....
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
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.
void ClearLocalRatsnest()
Erase the temporary, selection-based ratsnest (i.e.
void BlockRatsnestItems(const std::vector< BOARD_ITEM * > &aItems)
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A set of EDA_ITEMs (i.e., without duplicates).
std::unordered_set< EDA_ITEM * > & GetItems()
void AddItem(EDA_ITEM *aItem)
Add item to group.
bool ContainsItem(const EDA_ITEM *aItem) const
Test if an item is a direct or nested member of this group.
virtual EDA_ITEM * AsEdaItem()=0
A base class for most all the KiCad significant classes used in schematics and boards.
virtual EDA_GROUP * GetParentGroup() const
KICAD_T Type() const
Returns the type of object.
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
virtual void SetParentGroup(EDA_GROUP *aGroup)
virtual const wxString & GetText() const
Return the string associated with the text object.
virtual bool IsVisible() const
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
virtual void SetVisible(bool aVisible)
const TEXT_ATTRIBUTES & GetAttributes() const
A color representation with 4 components: red, green, blue, alpha.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
wxString AsString() const
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.
bool ContainsAll(const LSET &aLayers) const
See if this layer set contains all layers in another set.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Handle the data for a net.
const wxString & GetNetname() const
void SetItems(BOARD_ITEM *a, BOARD_ITEM *b=nullptr)
static TOOL_ACTION unlinkLayout
static TOOL_ACTION repeatLayout
static TOOL_ACTION generatePlacementRuleAreas
static TOOL_ACTION expandToLargestTopology
static TOOL_ACTION updateLinkedLayouts
static TOOL_ACTION expandToMatchingTopology
static TOOL_ACTION selectItemInteractively
Selection of reference points/items.
void RedrawRatsnest()
Force refresh of the ratsnest visual representation.
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
PCB_GENERATOR * DeepClone() const
void Move(const VECTOR2I &aMoveVector) override
Move this object.
A set of BOARD_ITEMs (i.e., without duplicates).
void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const override
Invoke a function on all children.
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
EDA_ITEM * AsEdaItem() override
EDA_ANGLE GetTextAngle() const override
VECTOR2I GetPosition() const override
void SetPosition(const VECTOR2I &aPos) override
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetTextAngle(const EDA_ANGLE &aAngle) override
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
A small class to help profiling.
void Stop()
Save the time when this function was called, and set the counter stane to stop.
static SELECTION_CONDITION HasTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if among the selected items there is at least one of a given types.
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
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.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
virtual void CacheTriangulation(bool aSimplify=false, const TASK_SUBMITTER &aSubmitter={}, bool aBuildIndexes=false)
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
static std::unique_ptr< CONNECTION_GRAPH > BuildFromFootprintSet(const std::set< FOOTPRINT * > &aFps, const std::set< FOOTPRINT * > &aOtherChannelFps={}, const std::unordered_set< int > &aGlobalNets={})
Define a general 2D-vector/point.
Handle a list of polygons defining a copper zone.
void SetDoNotAllowPads(bool aEnable)
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
const REPEAT_LAYOUT_OPTIONS & GetRepeatOptions() const
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
wxString GetPlacementAreaSource() const
void SetRepeatOptions(const REPEAT_LAYOUT_OPTIONS &aOpts)
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
void SetLinkSet(const KIID &aLinkSet)
PLACEMENT_SOURCE_T GetPlacementAreaSourceType() const
SHAPE_POLY_SET * Outline()
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
void SetIsRuleArea(bool aEnable)
void SetDoNotAllowTracks(bool aEnable)
const wxString & GetZoneName() const
void SetLayerSet(const LSET &aLayerSet) override
bool GetPlacementAreaEnabled() const
void SetDoNotAllowVias(bool aEnable)
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
void SetDoNotAllowFootprints(bool aEnable)
const KIID & GetLinkSet() const
The set of placement rule areas that share one layout, or niluuid when this one is not linked.
void SetDoNotAllowZoneFills(bool aEnable)
void UnHatchBorder()
Clear the zone's hatch.
void RemoveAllContours(void)
void SetPlacementAreaEnabled(bool aEnabled)
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
static constexpr EDA_ANGLE ANGLE_0
SHAPE_LINE_CHAIN RectifyPolygon(const SHAPE_LINE_CHAIN &aPoly)
Take a polygon and 'rectify' it, so that all sides are H/V.
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
size_t CommonPathTailLength(const KIID_PATH &aA, const KIID_PATH &aB)
Count the trailing elements two symbol instance paths share.
Class to handle a set of BOARD_ITEMs.
PGM_BASE & Pgm()
The global program "get" accessor.
std::vector< EDA_ITEM * > EDA_ITEMS
Utility functions for working with shapes.
void AccumulateDescription(wxString &aDesc, const wxString &aItem)
Utility to build comma separated lists in messages.
TMATCH::COMPONENT_MATCHES m_map
bool bind(FOOTPRINT *aRef, FOOTPRINT *aTarget, std::vector< int > &aAdded)
const std::vector< FOOTPRINT * > & m_order
bool extend(size_t aIndex)
const std::set< int > & m_internal
std::set< int > m_netsTaken
const std::map< int, std::vector< FOOTPRINT * > > & m_netOwners
bool pair(size_t aIndex, FOOTPRINT *aChoice)
const std::set< FOOTPRINT * > & m_leaves
std::map< int, int > m_netMap
const std::set< FOOTPRINT * > & m_claimed
std::set< FOOTPRINT * > m_used
Options for copying a placement rule area onto the others linked to it.
bool m_connectedRoutingOnly
KIID m_anchor
This area's own anchor footprint, or niluuid for the area center.
bool m_includeLockedItems
std::vector< wxString > m_mismatchReasons
TMATCH::COMPONENT_MATCHES m_discovered
Pairing found by discovery.
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::set< FOOTPRINT * > m_unmatched
Selected footprints the channel lacks.
std::unordered_set< BOARD_ITEM * > m_groupableItems
Filled in by copyRuleAreaContents with affected items that can be grouped together.
bool ScansContents() const
True when the contents are scanned from the area rather than taken from an exact item list.
std::unordered_set< EDA_ITEM * > m_designBlockItems
bool m_fromSelection
The reference of a plain selection, copied as given with its footprint area deciding what else comes ...
PLACEMENT_SOURCE_T m_sourceType
wxString m_componentClass
std::set< FOOTPRINT * > m_components
std::atomic< bool > * m_cancelled
std::atomic< int > * m_matchedComponents
std::atomic< int > * m_totalComponents
IbisParser parser & reporter
wxString result
Test unit parsing edge cases and error handling.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
@ PCB_ZONE_T
class ZONE, a copper pour area
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I