65 for(
RULE_AREA& ra : aTool->GetData()->m_areas )
67 if( ra.m_ruleName.Contains( ( aName ) ) )
76 if( aPath.EndsWith( wxT(
"/" ) ) )
84 wxString
path = ra.m_sheetPath;
86 if(
path.EndsWith( wxT(
"/" ) ) )
111 if( ra.m_zone && ra.m_zone->GetPlacementAreaSource() == aGroupName )
125 if( gen->GetGeneratorType() != wxT(
"tuning_pattern" ) )
128 if( gen->HitTest( outline,
false ) )
145 if( aRAZone->
Outline()->
Contains( zone->Outline()->COutline( 0 ).Centre() ) )
157 for(
const ZONE* zone : aBoard->
Zones() )
159 if( zone == aRuleArea.
m_zone )
162 if( zone->GetZoneName() != aZoneName )
177 for(
const ZONE* zone : aBoard->
Zones() )
179 if( zone == aRuleArea.
m_zone )
182 const wxString&
name = zone->GetZoneName();
185 if(
name != aBaseName && !
name.StartsWith( aBaseName + wxT(
"_" ) ) )
200 std::vector<wxString> tests = {
"vme-wren" };
202 for(
const wxString& relPath : tests )
209 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
218 auto ruleData = mtTool->
GetData();
221 static_cast<int>( ruleData->m_areas.size() ) ) );
228 ruleData->m_replaceExisting =
true;
233 || ra.
m_sheetName == wxT(
"pp_driver_2x.kicad_sch" ) )
247 int n_areas_io = 0, n_areas_pp = 0, n_areas_other = 0;
250 static_cast<int>(ruleData->m_areas.size() ) ) );
252 for(
const RULE_AREA& ra : ruleData->m_areas )
256 if( ra.
m_ruleName.Contains( wxT(
"io_drivers_fp" ) ) )
261 else if( ra.
m_ruleName.Contains( wxT(
"io_drivers_pp" ) ) )
273 n_areas_io, n_areas_pp, n_areas_other ) );
279 const std::vector<wxString> rulesToTest = { wxT(
"io_drivers_fp" ),
280 wxT(
"io_drivers_pp" ) };
282 for(
const wxString& ruleName : rulesToTest )
284 for(
const RULE_AREA& refArea : ruleData->m_areas )
286 if( !refArea.
m_ruleName.Contains( ruleName ) )
291 for(
const RULE_AREA& targetArea : ruleData->m_areas )
296 if( !targetArea.
m_ruleName.Contains( ruleName ) )
299 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea.
m_components,
302 CONNECTION_GRAPH::BuildFromFootprintSet( targetArea.
m_components,
307 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
308 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
311 "topo match: '%s' [%d] -> '%s' [%d] result %d", refArea.
m_ruleName.c_str().AsChar(),
313 static_cast<int>( targetArea.
m_components.size() ), status ? 1 : 0 ) );
315 for(
const auto& iter :
result )
318 iter.second->GetReference(),
319 iter.first->GetReference() ) );
322 BOOST_CHECK( status );
323 BOOST_CHECK( details.empty() );
330 BOOST_ASSERT( refArea );
332 const std::vector<wxString> targetAreaNames( { wxT(
"io_drivers_fp/bank2/io78/" ),
333 wxT(
"io_drivers_fp/bank1/io78/" ),
334 wxT(
"io_drivers_fp/bank0/io01/" ) } );
336 for(
const wxString& targetRaName : targetAreaNames )
340 BOOST_ASSERT( targetRA !=
nullptr );
344 ruleData->m_compatMap[targetRA].m_doCopy =
true;
349 BOOST_ASSERT(
result >= 0 );
365 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
372 auto ruleData = mtTool->
GetData();
375 static_cast<int>( ruleData->m_areas.size() ) ) );
377 BOOST_CHECK( ruleData->m_areas.size() >= 2 );
379 if( ruleData->m_areas.size() < 2 )
387 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
389 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
393 if( !refArea || !targetArea )
395 BOOST_TEST_MESSAGE(
"Could not find Untitled Sheet and Untitled Sheet1 rule areas, skipping test" );
423 if( !refFP || !targetFP )
425 BOOST_TEST_MESSAGE(
"Could not find matching footprints in the rule areas, skipping test" );
430 bool refValueVisible = refValueField ? refValueField->
IsVisible() :
true;
432 std::vector<FP_3DMODEL> refModels = refFP->
Models();
436 ruleData->m_compatMap[targetArea].m_doCopy =
true;
437 ruleData->m_options.m_copyPlacement =
true;
441 BOOST_CHECK(
result >= 0 );
445 if( targetValueField && refValueField )
449 refValueVisible, targetValueField->
IsVisible() ) );
454 if( !refModels.empty() )
457 static_cast<int>( refModels.size() ),
458 static_cast<int>( targetFP->
Models().size() ) ) );
477 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
484 auto ruleData = mtTool->
GetData();
487 static_cast<int>( ruleData->m_areas.size() ) ) );
491 if( ruleData->m_areas.size() < 2 )
508 int refViaCountBefore = 0;
510 for(
PCB_TRACK* track : m_board->Tracks() )
522 BOOST_TEST_MESSAGE( wxString::Format(
"Reference area vias before repeat: %d", refViaCountBefore ) );
523 BOOST_CHECK( refViaCountBefore > 0 );
527 ruleData->m_compatMap[targetArea].m_doCopy =
true;
528 ruleData->m_options.m_copyPlacement =
true;
529 ruleData->m_options.m_copyRouting =
true;
533 BOOST_CHECK(
result >= 0 );
535 int refViaCountAfter = 0;
537 for(
PCB_TRACK* track : m_board->Tracks() )
549 BOOST_TEST_MESSAGE( wxString::Format(
"Reference area vias after repeat: %d", refViaCountAfter ) );
566 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
631 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
632 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
635 LIB_ID fpid( wxT(
"Package_SO" ), wxT(
"SOIC-8_3.9x4.9mm_P1.27mm" ) );
647 PAD padRef1( &fpRef );
650 fpRef.
Add( &padRef1 );
652 PAD padRef2( &fpRef );
655 fpRef.
Add( &padRef2 );
657 PAD padTarget1( &fpTarget );
660 fpTarget.
Add( &padTarget1 );
662 PAD padTarget2( &fpTarget );
665 fpTarget.
Add( &padTarget2 );
668 cgRef->AddFootprint( &fpRef,
VECTOR2I( 0, 0 ) );
669 cgTarget->AddFootprint( &fpTarget,
VECTOR2I( 0, 0 ) );
671 cgRef->BuildConnectivity();
672 cgTarget->BuildConnectivity();
678 COMPONENT* cmpRef = cgRef->Components()[0];
679 COMPONENT* cmpTarget = cgTarget->Components()[0];
681 bool sameKind = cmpRef->IsSameKind( *cmpTarget );
683 BOOST_TEST_MESSAGE( wxString::Format(
"TRIM_1.1 and TRIM_2.1 IsSameKind: %d", sameKind ? 1 : 0 ) );
684 BOOST_CHECK( sameKind );
688 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
689 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
691 BOOST_TEST_MESSAGE( wxString::Format(
"Topology match result: %d", status ? 1 : 0 ) );
693 if( !status && !details.empty() )
695 for(
const auto& reason : details )
698 reason.m_reference, reason.m_candidate, reason.m_reason ) );
702 BOOST_CHECK( status );
703 BOOST_CHECK( details.empty() );
706 fpRef.
Pads().clear();
707 fpTarget.
Pads().clear();
722 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
723 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
725 LIB_ID fpid( wxT(
"_BugFpLib" ), wxT(
"SW_Hotswap" ) );
738 PAD padRef1( &fpRef );
741 fpRef.
Add( &padRef1 );
743 PAD padRef2( &fpRef );
746 fpRef.
Add( &padRef2 );
748 PAD padTarget1( &fpTarget );
751 fpTarget.
Add( &padTarget1 );
753 PAD padTarget2( &fpTarget );
756 fpTarget.
Add( &padTarget2 );
758 cgRef->AddFootprint( &fpRef,
VECTOR2I( 0, 0 ) );
759 cgTarget->AddFootprint( &fpTarget,
VECTOR2I( 0, 0 ) );
761 cgRef->BuildConnectivity();
762 cgTarget->BuildConnectivity();
767 COMPONENT* cmpRef = cgRef->Components()[0];
768 COMPONENT* cmpTarget = cgTarget->Components()[0];
770 BOOST_CHECK_MESSAGE( cmpRef->IsSameKind( *cmpTarget ),
771 "REF** placeholder must be the same kind as an annotated footprint "
772 "with matching FPID (issue 24585)" );
775 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
776 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
780 for(
const auto& reason : details )
783 reason.m_reference, reason.m_candidate, reason.m_reason ) );
787 BOOST_CHECK_MESSAGE( status,
788 "Design block layout with an un-annotated REF** footprint must match "
789 "an annotated destination (issue 24585)" );
790 BOOST_CHECK( details.empty() );
792 fpRef.
Pads().clear();
793 fpTarget.
Pads().clear();
806 LIB_ID fpid( wxT(
"_BugFpLib" ), wxT(
"SW_Hotswap" ) );
807 LIB_ID otherFpid( wxT(
"_BugFpLib" ), wxT(
"R_0402" ) );
820 BOOST_CHECK_MESSAGE( !cmpUnrelated.IsSameKind( cmpTarget ),
821 "Two annotated footprints with unrelated prefixes must not match even "
822 "with the same FPID (issue 24585)" );
826 fpPlaceholder.
SetFPID( otherFpid );
830 BOOST_CHECK_MESSAGE( !cmpPlaceholder.IsSameKind( cmpTarget ),
831 "A REF** placeholder must not match a target with a different FPID "
851 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
858 auto ruleData = mtTool->
GetData();
866 if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/bank0/io01/" ) )
868 else if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/bank0/" ) )
870 else if( ra.
m_sheetPath == wxT(
"/io_drivers_fp/" ) )
878 BOOST_TEST_MESSAGE( wxString::Format(
"Leaf /io_drivers_fp/bank0/io01/ components: %d",
917 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
924 auto ruleData = mtTool->
GetData();
930 if( ra.
m_sheetName == wxT(
"io_driver.kicad_sch" ) )
944 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->
m_components,
946 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->
m_components,
951 std::atomic<bool> cancelled(
true );
952 std::atomic<int> matched( 0 );
953 std::atomic<int> total( 0 );
961 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
963 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details, params );
965 BOOST_CHECK( !status );
966 BOOST_CHECK(
result.empty() );
973 std::atomic<bool> cancelled(
false );
974 std::atomic<int> matched( 0 );
975 std::atomic<int> total( 0 );
983 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
985 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details, params );
987 BOOST_CHECK( status );
989 int finalMatched = matched.load();
990 int finalTotal = total.load();
992 BOOST_TEST_MESSAGE( wxString::Format(
"Progress: matched=%d, total=%d", finalMatched, finalTotal ) );
994 BOOST_CHECK( finalTotal > 0 );
1001 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1003 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
1005 BOOST_CHECK( status );
1006 BOOST_CHECK( !
result.empty() );
1032 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1039 auto ruleData = mtTool->
GetData();
1042 static_cast<int>( ruleData->m_areas.size() ) ) );
1049 for(
RULE_AREA& ra : ruleData->m_areas )
1056 if( source == wxT(
"/i2c_thingy_ch0/" ) )
1058 else if( source == wxT(
"/i2c_thingy_ch1/" ) )
1062 BOOST_REQUIRE_MESSAGE( ch0Area !=
nullptr,
"Could not find i2c_thingy_ch0 rule area" );
1063 BOOST_REQUIRE_MESSAGE( ch1Area !=
nullptr,
"Could not find i2c_thingy_ch1 rule area" );
1071 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( ch0Area->
m_components,
1073 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( ch1Area->
m_components,
1077 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1078 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
1082 for(
const auto& reason : details )
1085 reason.m_reference, reason.m_candidate,
1086 reason.m_reason ) );
1090 BOOST_CHECK_MESSAGE( status,
1091 "Topology match failed for channels with hierarchical pins "
1092 "tied to global nets (issue 21739)" );
1106 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
1109 auto addNet = [&](
const wxString& aName ) ->
int
1116 const int netGnd = addNet( wxT(
"GND" ) );
1117 const int netVcc = addNet( wxT(
"+5VP" ) );
1118 const int netChainInRef = addNet( wxT(
"Net-(D-RefChainIn)" ) );
1119 const int netBridge = addNet( wxT(
"Net-(D38-DOUT)" ) );
1120 const int netChainOutTgt = addNet( wxT(
"Net-(D43-DOUT)" ) );
1121 const int netD2Dout = addNet( wxT(
"unconnected-(D2-DOUT)" ) );
1122 const int netD3Dout = addNet( wxT(
"unconnected-(D3-DOUT)" ) );
1123 const int netD5Dout = addNet( wxT(
"unconnected-(D5-DOUT)" ) );
1124 const int netD6Dout = addNet( wxT(
"unconnected-(D6-DOUT)" ) );
1126 LIB_ID ledId( wxT(
"TestLib" ), wxT(
"WS2812" ) );
1130 [&](
const wxString& aRef,
int aDout,
int aDin ) ->
FOOTPRINT*
1138 [&](
const wxString& aNumber,
int aNetCode )
1142 pad->SetNumber( aNumber );
1147 pad->SetNetCode( aNetCode );
1151 addPad( wxT(
"1" ), aDout );
1152 addPad( wxT(
"2" ), netGnd );
1153 addPad( wxT(
"3" ), aDin );
1154 addPad( wxT(
"4" ), netVcc );
1161 std::set<FOOTPRINT*> refFps;
1162 refFps.insert( makeLed( wxT(
"D1" ), netBridge, netChainInRef ) );
1163 refFps.insert( makeLed( wxT(
"D2" ), netD2Dout, netChainInRef ) );
1164 refFps.insert( makeLed( wxT(
"D3" ), netD3Dout, netChainInRef ) );
1168 std::set<FOOTPRINT*> tgtFps;
1169 tgtFps.insert( makeLed( wxT(
"D4" ), netChainOutTgt, netBridge ) );
1170 tgtFps.insert( makeLed( wxT(
"D5" ), netD5Dout, netBridge ) );
1171 tgtFps.insert( makeLed( wxT(
"D6" ), netD6Dout, netBridge ) );
1173 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refFps, tgtFps );
1174 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( tgtFps, refFps );
1177 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1178 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
1182 for(
const auto& reason : details )
1185 reason.m_reference, reason.m_candidate,
1186 reason.m_reason ) );
1190 BOOST_CHECK_MESSAGE( status,
1191 "Topology match failed because the design-block boundary signal net "
1192 "was misclassified as a global rail and excluded asymmetrically" );
1206 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
1208 auto addNet = [&](
const wxString& aName ) ->
int
1215 const int netGnd = addNet( wxT(
"GND" ) );
1216 const int netVcc = addNet( wxT(
"+3V3" ) );
1218 const LIB_ID cap0201( wxT(
"Capacitor_SMD" ), wxT(
"C_0201_0603Metric" ) );
1219 const LIB_ID cap0402( wxT(
"Capacitor_SMD" ), wxT(
"C_0402_1005Metric" ) );
1221 auto makeCap = [&](
const wxString& aRef,
const LIB_ID& aFpId ) ->
FOOTPRINT*
1228 for(
const wxString& padNumber : { wxString( wxT(
"1" ) ), wxString( wxT(
"2" ) ) } )
1231 pad->SetNumber( padNumber );
1235 pad->SetNetCode( padNumber == wxT(
"1" ) ? netVcc : netGnd );
1244 std::set<FOOTPRINT*> blockFps;
1245 std::set<FOOTPRINT*> groupFps;
1247 for(
int i = 0; i < 8; i++ )
1249 blockFps.insert( makeCap( wxString::Format( wxT(
"C%d" ), 600 + i ), cap0201 ) );
1250 groupFps.insert( makeCap( wxString::Format( wxT(
"C%d" ), 600 + i ), cap0201 ) );
1253 blockFps.insert( makeCap( wxT(
"C619" ), cap0201 ) );
1254 groupFps.insert( makeCap( wxT(
"C619" ), cap0402 ) );
1256 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( blockFps, groupFps );
1257 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( groupFps, blockFps );
1260 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1262 BOOST_CHECK( !cgRef->FindIsomorphism( cgTarget.get(),
result, details ) );
1265 for(
const auto& reason : details )
1267 BOOST_TEST_MESSAGE( wxString::Format(
"Mismatch: %s <-> %s: %s", reason.m_reference, reason.m_candidate,
1268 reason.m_reason ) );
1287 m_board = std::make_unique<BOARD>();
1292 m_board->Add( net );
1294 auto makeFootprint =
1303 pad->SetNumber( wxT(
"1" ) );
1305 pad->SetPosition( aPos );
1317 FOOTPRINT* refFp1 = makeFootprint( wxT(
"R1" ),
1319 FOOTPRINT* refFp2 = makeFootprint( wxT(
"R2" ),
1327 m_board->Add( silkRect );
1330 FOOTPRINT* destFp1 = makeFootprint( wxT(
"R3" ),
1332 FOOTPRINT* destFp2 = makeFootprint( wxT(
"R4" ),
1336 destGroup->
SetName( wxT(
"design-block-dest" ) );
1337 destGroup->
AddItem( destFp1 );
1338 destGroup->
AddItem( destFp2 );
1339 m_board->Add( destGroup );
1348 m_board->Add( unrelatedSilk );
1352 for(
BOARD_ITEM* item : m_board->Drawings() )
1358 BOOST_REQUIRE_EQUAL( silkBefore, 2 );
1391 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1397 .m_connectedRoutingOnly =
false,
1398 .m_copyPlacement =
true,
1399 .m_copyOtherItems =
true,
1400 .m_groupItems =
false,
1401 .m_includeLockedItems =
true,
1402 .m_anchorFp =
nullptr };
1405 BOOST_CHECK_MESSAGE(
result >= 0,
"RepeatLayout failed" );
1414 int silkInGroup = 0;
1415 bool unrelatedSurvived =
false;
1418 for(
BOARD_ITEM* item : m_board->Drawings() )
1425 if( item == unrelatedSilk )
1426 unrelatedSurvived =
true;
1428 if( item->GetParentGroup() == destGroup )
1439 BOOST_CHECK_MESSAGE( silkAfter == 3,
1440 wxString::Format(
"Expected 3 silkscreen shapes after Apply Design Block "
1441 "Layout (2 original + 1 copy), found %d (issue 24372)",
1443 BOOST_CHECK_MESSAGE( silkInGroup == 1,
1444 wxString::Format(
"Expected 1 silkscreen shape in destination group, "
1445 "found %d (issue 24372)",
1447 BOOST_CHECK_MESSAGE( unrelatedSurvived,
1448 "Unrelated silkscreen drawing outside the design block group was "
1449 "deleted by Apply Design Block Layout (issue 24372)" );
1465 m_board = std::make_unique<BOARD>();
1469 m_board->Add( net );
1471 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos ) ->
FOOTPRINT*
1479 pad->SetNumber( wxT(
"1" ) );
1481 pad->SetPosition( aPos );
1497 for(
FOOTPRINT* fp : { refFp1, refFp2 } )
1498 fp->GetField(
FIELD_T::VALUE )->SetTextAngle( sourceFieldAngle );
1504 for(
FOOTPRINT* fp : { destFp1, destFp2 } )
1505 fp->SetOrientation( blockRotation );
1508 destGroup->
SetName( wxT(
"design-block-dest" ) );
1509 destGroup->
AddItem( destFp1 );
1510 destGroup->
AddItem( destFp2 );
1511 m_board->Add( destGroup );
1541 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1547 .m_connectedRoutingOnly =
false,
1548 .m_copyPlacement =
true,
1549 .m_copyOtherItems =
true,
1550 .m_groupItems =
false,
1551 .m_includeLockedItems =
true,
1552 .m_anchorFp =
nullptr };
1555 BOOST_REQUIRE_MESSAGE(
result >= 0,
"RepeatLayout failed" );
1562 const double expected = ( sourceFieldAngle + blockRotation ).AsDegrees();
1564 for(
FOOTPRINT* fp : { destFp1, destFp2 } )
1572 wxString::Format(
"Field on %s rotated to %.3f deg, expected %.3f deg "
1573 "(field double-rotation regression)",
1588 m_board = std::make_unique<BOARD>();
1592 m_board->Add( shared );
1594 auto makeFootprint =
1595 [&](
const wxString& aRef,
const VECTOR2I& aPos,
const KIID& aSymbolUuid,
1599 fp->
SetFPID(
LIB_ID( wxT(
"TestLib" ), wxT(
"Receptacle" ) ) );
1605 path.push_back( aSymbolUuid );
1610 pad->SetNumber( wxT(
"1" ) );
1612 pad->SetPosition( aPos );
1617 pad->SetNet( aNet );
1627 FOOTPRINT* refFpA = makeFootprint( wxT(
"J5" ),
VECTOR2I( 0, 0 ), symA,
nullptr );
1638 std::set<FOOTPRINT*> refSet{ refFpA, refFpB };
1639 std::set<FOOTPRINT*> destSet{ destFpA, destFpB };
1640 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refSet, destSet );
1641 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( destSet, refSet );
1643 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
1644 BOOST_REQUIRE_MESSAGE( !cgRef->FindIsomorphism( cgTarget.get(), topoOnly, details ),
1645 "Test precondition broken: areas are net isomorphic" );
1649 destGroup->
SetName( wxT(
"design-block-dest" ) );
1650 destGroup->
AddItem( destFpA );
1651 destGroup->
AddItem( destFpB );
1652 m_board->Add( destGroup );
1681 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1686 .m_connectedRoutingOnly =
false,
1687 .m_copyPlacement =
true,
1688 .m_copyOtherItems =
true,
1689 .m_groupItems =
false,
1690 .m_includeLockedItems =
true,
1691 .m_anchorFp =
nullptr };
1698 BOOST_CHECK_MESSAGE(
result >= 0,
"Apply Design Block Layout aborted even though the symbol instance paths "
1699 "give an unambiguous mapping (No compatible component regression)" );
1704 wxString::Format(
"Destination relative placement wrong, expected "
1705 "(10mm,0) got (%d,%d) IU",
1720 m_board = std::make_unique<BOARD>();
1724 m_board->Add( net );
1726 auto makeFootprint = [&](
const wxString& aRef,
const VECTOR2I& aPos ) ->
FOOTPRINT*
1737 txt->
SetText( wxT(
"${REFERENCE}" ) );
1744 pad->SetNumber( wxT(
"1" ) );
1746 pad->SetPosition( aPos );
1761 if( item->Type() ==
PCB_TEXT_T && item->GetLayer() == layer )
1775 destGroup->
SetName( wxT(
"design-block-dest" ) );
1776 destGroup->
AddItem( destFp1 );
1777 destGroup->
AddItem( destFp2 );
1778 m_board->Add( destGroup );
1780 BOOST_REQUIRE_EQUAL( countSilkText( destFp1,
F_SilkS ), 1 );
1781 BOOST_REQUIRE_EQUAL( countSilkText( destFp1,
B_SilkS ), 1 );
1811 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1817 .m_connectedRoutingOnly =
false,
1818 .m_copyPlacement =
true,
1819 .m_copyOtherItems =
true,
1820 .m_groupItems =
false,
1821 .m_includeLockedItems =
true,
1822 .m_anchorFp =
nullptr };
1826 BOOST_CHECK_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
1831 for(
FOOTPRINT* destFp : { destFp1, destFp2 } )
1833 BOOST_CHECK_MESSAGE( countSilkText( destFp,
F_SilkS ) == 1,
1834 wxString::Format(
"%s lost its F.Silkscreen reference text "
1835 "(issue 24583): F=%d B=%d",
1836 destFp->GetReference(), countSilkText( destFp,
F_SilkS ),
1837 countSilkText( destFp,
B_SilkS ) ) );
1849 m_board = std::make_unique<BOARD>();
1873 m_board->Add( destPlaceholder );
1876 destGroup->
SetName( wxT(
"design-block-dest" ) );
1877 destGroup->
AddItem( destPlaceholder );
1878 m_board->Add( destGroup );
1882 for(
BOARD_ITEM* item : m_board->Drawings() )
1888 BOOST_REQUIRE_EQUAL( silkBefore, 2 );
1916 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
1922 .m_connectedRoutingOnly =
false,
1923 .m_copyPlacement =
true,
1924 .m_copyOtherItems =
true,
1925 .m_groupItems =
false,
1926 .m_includeLockedItems =
true,
1927 .m_anchorFp =
nullptr };
1935 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed for a footprint-free "
1936 "(graphics-only) design block (issue 24592): %s",
1944 for(
BOARD_ITEM* item : m_board->Drawings() )
1957 BOOST_CHECK_MESSAGE( silkAfter == 2, wxString::Format(
"Expected 2 silkscreen shapes after Apply Design Block "
1958 "Layout (source + 1 copy, placeholder replaced), found "
1961 BOOST_REQUIRE_MESSAGE( copiedRect !=
nullptr,
"Footprint-free block graphic was not copied (issue 24592)" );
1963 "Copied block graphic was not added to the destination group (issue 24592)" );
1974 m_board = std::make_unique<BOARD>();
1978 m_board->Add( net );
1987 m_board->Add( srcTrack );
1989 BOOST_REQUIRE_EQUAL( srcTrack->
GetNetCode(), 1 );
1997 m_board->Add( destPlaceholder );
2000 destGroup->
SetName( wxT(
"design-block-dest" ) );
2001 destGroup->
AddItem( destPlaceholder );
2002 m_board->Add( destGroup );
2030 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2036 .m_connectedRoutingOnly =
false,
2037 .m_copyPlacement =
true,
2038 .m_copyOtherItems =
true,
2039 .m_groupItems =
false,
2040 .m_includeLockedItems =
true,
2041 .m_anchorFp =
nullptr };
2049 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed for a footprint-free copper "
2050 "block (issue 24592): %s",
2056 for(
PCB_TRACK* track : m_board->Tracks() )
2058 if( track != srcTrack )
2059 copiedTrack = track;
2062 BOOST_REQUIRE_MESSAGE( copiedTrack !=
nullptr,
"Footprint-free block track was not copied (issue 24592)" );
2063 BOOST_CHECK_MESSAGE( copiedTrack->
GetNetCode() == 0,
2064 wxString::Format(
"Copied copper should be no-net, got net code %d "
2068 "Copied copper was not added to the destination group (issue 24592)" );
2083 m_board = std::make_unique<BOARD>();
2092 m_board->Add( srcRect );
2100 m_board->Add( destPlaceholder );
2103 destGroup->
SetName( wxT(
"design-block-dest" ) );
2104 destGroup->
AddItem( destPlaceholder );
2105 m_board->Add( destGroup );
2109 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2114 auto applyOnce = [&]() ->
int
2141 .m_connectedRoutingOnly =
false,
2142 .m_copyPlacement =
true,
2143 .m_copyOtherItems =
true,
2144 .m_groupItems =
false,
2145 .m_includeLockedItems =
true,
2146 .m_anchorFp =
nullptr };
2155 BOOST_REQUIRE_MESSAGE( applyOnce() >= 0,
"First apply failed (issue 24592)" );
2156 BOOST_REQUIRE_MESSAGE( applyOnce() >= 0,
"Second apply failed (issue 24592)" );
2162 for(
BOARD_ITEM* item : m_board->Drawings() )
2171 BOOST_CHECK_MESSAGE( rects == 2, wxString::Format(
"Re-apply stacked copies: expected 2 rectangles "
2172 "(source + 1 copy), found %d (issue 24592)",
2186 m_board = std::make_unique<BOARD>();
2190 m_board->Add( net );
2199 m_board->Add( srcTrack );
2207 m_board->Add( destPlaceholder );
2210 destGroup->
SetName( wxT(
"design-block-dest" ) );
2211 destGroup->
AddItem( destPlaceholder );
2212 m_board->Add( destGroup );
2220 m_board->Add( siblingTrack );
2223 siblingGroup->
SetName( wxT(
"design-block-sibling" ) );
2224 siblingGroup->
AddItem( siblingTrack );
2225 m_board->Add( siblingGroup );
2233 m_board->Add( looseTrack );
2261 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2267 .m_connectedRoutingOnly =
false,
2268 .m_copyPlacement =
true,
2269 .m_copyOtherItems =
true,
2270 .m_groupItems =
false,
2271 .m_includeLockedItems =
true,
2272 .m_anchorFp =
nullptr };
2280 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
2283 int siblingTracks = 0;
2284 int looseTracks = 0;
2286 for(
PCB_TRACK* track : m_board->Tracks() )
2288 int y = track->GetStart().y;
2296 BOOST_CHECK_MESSAGE( siblingTracks == 1,
2297 wxString::Format(
"Sibling group routing was deleted (issue 24767): found %d, expected 1",
2299 BOOST_CHECK_MESSAGE( looseTracks == 1,
2300 wxString::Format(
"Loose no-net routing in the target area was deleted (issue 24944): "
2301 "found %d, expected 1",
2313 m_board = std::make_unique<BOARD>();
2318 m_board->Add( net1 );
2319 m_board->Add( net2 );
2321 auto addTrack = [&](
double aStartX,
double aStartY,
double aEndX,
double aEndY,
NETINFO_ITEM* aNet )
2329 m_board->Add( track );
2334 PCB_TRACK* srcTrack = addTrack( -2, 0, 2, 0, net1 );
2342 m_board->Add( destPlaceholder );
2344 PCB_TRACK* prevTrack = addTrack( 46, 49, 48, 49, net1 );
2347 destGroup->
SetName( wxT(
"design-block-dest" ) );
2348 destGroup->
AddItem( destPlaceholder );
2349 destGroup->
AddItem( prevTrack );
2350 m_board->Add( destGroup );
2354 addTrack( 46, 47, 48, 47, net2 );
2356 PCB_TRACK* lockedTrack = addTrack( 46, 51.5, 48, 51.5, net1 );
2360 addTrack( 46, 53, 48, 53, net1 );
2388 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2394 .m_connectedRoutingOnly =
false,
2395 .m_copyPlacement =
true,
2396 .m_copyOtherItems =
true,
2397 .m_groupItems =
false,
2398 .m_includeLockedItems =
true,
2399 .m_anchorFp =
nullptr };
2407 BOOST_REQUIRE_MESSAGE(
result >= 0, wxString::Format(
"RepeatLayout failed: %s", err ) );
2410 int unrelatedTracks = 0;
2412 int copiedTracks = 0;
2413 int lockedTracks = 0;
2414 int staleTweakTracks = 0;
2416 for(
PCB_TRACK* track : m_board->Tracks() )
2418 int y = track->GetStart().y;
2432 BOOST_CHECK_MESSAGE( unrelatedTracks == 1,
2433 wxString::Format(
"Unrelated net routing in the target area was deleted (issue 24944): "
2434 "found %d, expected 1",
2435 unrelatedTracks ) );
2436 BOOST_CHECK_MESSAGE( lockedTracks == 1,
2437 wxString::Format(
"Locked bystander routing in the target area was deleted (issue 24944): "
2438 "found %d, expected 1",
2440 BOOST_CHECK_MESSAGE(
2442 wxString::Format(
"The group's own routing should be replaced: found %d, expected 0", prevTracks ) );
2443 BOOST_CHECK_MESSAGE( staleTweakTracks == 0,
2444 wxString::Format(
"Loose routing on the group's nets should be replaced: found %d, "
2446 staleTweakTracks ) );
2447 BOOST_CHECK_MESSAGE( copiedTracks == 1,
2448 wxString::Format(
"Block routing was not copied into the target area: found %d, expected 1",
2466 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2473 auto ruleData = mtTool->
GetData();
2476 static_cast<int>( ruleData->m_areas.size() ) ) );
2478 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 4 );
2481 const size_t sharedCount = ruleData->m_areas.front().m_components.size();
2485 for(
const RULE_AREA& ra : ruleData->m_areas )
2488 RULE_AREA* refArea = &ruleData->m_areas.front();
2492 BOOST_REQUIRE_EQUAL( ruleData->m_compatMap.size(), ruleData->m_areas.size() - 1 );
2494 for(
const auto& [targetArea, compatData] : ruleData->m_compatMap )
2496 BOOST_CHECK_MESSAGE( !compatData.m_isOk,
2497 "Duplicate placement area was wrongly reported as a valid copy target "
2499 BOOST_CHECK( !compatData.m_mismatchReasons.empty() );
2503 RULE_AREA* targetArea = &ruleData->m_areas[1];
2510 BOOST_CHECK_MESSAGE(
result < 0,
2511 "RepeatLayout copied a Rule Area onto an identical-component area "
2513 BOOST_CHECK( !err.IsEmpty() );
2535 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2542 auto ruleData = mtTool->
GetData();
2545 static_cast<int>( ruleData->m_areas.size() ) ) );
2547 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 4 );
2551 for(
RULE_AREA& ra : ruleData->m_areas )
2559 std::set<KIID> groupUuidsBefore;
2560 std::set<wxString> userGroupNamesBefore;
2564 groupUuidsBefore.insert(
group->m_Uuid );
2566 if( !
group->GetName().IsEmpty() )
2567 userGroupNamesBefore.insert(
group->GetName() );
2572 for(
auto& [targetArea, compatData] : ruleData->m_compatMap )
2573 compatData.m_doCopy =
true;
2575 ruleData->m_options.m_copyPlacement =
true;
2576 ruleData->m_options.m_copyRouting =
true;
2577 ruleData->m_options.m_copyOtherItems =
true;
2578 ruleData->m_options.m_groupItems =
true;
2579 ruleData->m_options.m_includeLockedItems =
true;
2585 int clonedUserGroups = 0;
2589 bool isNew = !groupUuidsBefore.contains(
group->m_Uuid );
2591 if( isNew && userGroupNamesBefore.contains(
group->GetName() ) )
2595 BOOST_CHECK_MESSAGE( clonedUserGroups == 0,
2596 wxString::Format(
"Repeat layout cloned %d unrelated user groups "
2598 clonedUserGroups ) );
2617 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
2624 auto ruleData = mtTool->
GetData();
2626 BOOST_REQUIRE_EQUAL( ruleData->m_areas.size(), 2 );
2631 for(
RULE_AREA& ra : ruleData->m_areas )
2633 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
2635 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
2645 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refArea->
m_components,
2647 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( targetArea->
m_components,
2651 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2652 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2655 BOOST_CHECK( !status );
2660 std::set<wxString> refRefDes;
2661 std::set<wxString> targetRefDes;
2669 bool sawConnectivityReason =
false;
2671 for(
const auto& reason : details )
2674 reason.m_candidate, reason.m_reason ) );
2678 BOOST_CHECK( !reason.m_reason.Contains( wxT(
"No compatible component found" ) ) );
2680 if( reason.m_reason.Contains( wxT(
"connects to" ) )
2681 || reason.m_reason.Contains( wxT(
"connectivity" ) ) )
2683 sawConnectivityReason =
true;
2687 BOOST_CHECK_MESSAGE( refRefDes.count( reason.m_reference ) > 0,
2688 wxString::Format(
"Reason reference '%s' should be a reference-area "
2689 "component", reason.m_reference ) );
2690 BOOST_CHECK_MESSAGE( targetRefDes.count( reason.m_candidate ) > 0,
2691 wxString::Format(
"Reason candidate '%s' should be a target-area "
2692 "component", reason.m_candidate ) );
2696 BOOST_CHECK_MESSAGE( sawConnectivityReason,
2697 "Topology mismatch message should explain the connectivity difference "
2698 "rather than report a generic missing-component error (issue 24192)" );
2712 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
2715 [&](
const wxString& aName ) ->
int
2722 const int netGnd = addNet( wxT(
"GND" ) );
2723 const int netP3V3 = addNet( wxT(
"+3V3" ) );
2724 const int netCollide = addNet( wxT(
"Net-(D3-A)" ) );
2725 const int netRefY = addNet( wxT(
"Net-(D4-A)" ) );
2726 const int netTgtX = addNet( wxT(
"Net-(D2-A)" ) );
2728 LIB_ID ledAId( wxT(
"TestLib" ), wxT(
"LED_A" ) );
2729 LIB_ID ledBId( wxT(
"TestLib" ), wxT(
"LED_B" ) );
2730 LIB_ID resId( wxT(
"TestLib" ), wxT(
"R" ) );
2733 [&](
FOOTPRINT* fp,
const wxString& aNum,
int aNet )
2737 pad->SetNumber( aNum );
2741 pad->SetNetCode( aNet );
2757 [&](
const LIB_ID& aLedId,
const wxString& aLedRef,
const wxString& aResRef,
int aAnodeNet,
2758 std::set<FOOTPRINT*>& aSet )
2760 FOOTPRINT* led = addFp( aLedId, aLedRef );
2761 addPad( led, wxT(
"1" ), netGnd );
2762 addPad( led, wxT(
"2" ), aAnodeNet );
2766 addPad(
res, wxT(
"1" ), aAnodeNet );
2767 addPad(
res, wxT(
"2" ), netP3V3 );
2772 std::set<FOOTPRINT*> refFps;
2773 addLedAndRes( ledAId, wxT(
"D3" ), wxT(
"R27" ), netCollide, refFps );
2774 addLedAndRes( ledBId, wxT(
"D4" ), wxT(
"R30" ), netRefY, refFps );
2777 std::set<FOOTPRINT*> tgtFps;
2778 addLedAndRes( ledAId, wxT(
"D2" ), wxT(
"R1" ), netTgtX, tgtFps );
2779 addLedAndRes( ledBId, wxT(
"D3" ), wxT(
"R2" ), netCollide, tgtFps );
2782 std::unordered_set<EDA_ITEM*> refItems( refFps.begin(), refFps.end() );
2785 auto cgRef = CONNECTION_GRAPH::BuildFromFootprintSet( refFps, tgtFps );
2786 auto cgTarget = CONNECTION_GRAPH::BuildFromFootprintSet( tgtFps, refFps );
2789 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2790 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2794 for(
const auto& reason : details )
2796 BOOST_TEST_MESSAGE( wxString::Format(
"Mismatch: %s to %s: %s", reason.m_reference, reason.m_candidate,
2797 reason.m_reason ) );
2801 BOOST_CHECK_MESSAGE( status,
"Topology match failed even after isolating the block's auto nets (issue 24767)" );
2815 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
2817 const LIB_ID receptacleId( wxT(
"Don-Con" ), wxT(
"Mill-Max-Pin_Receptacle" ) );
2820 auto makeReceptacle =
2821 [&](
const wxString& aRef,
const wxString& aValue,
bool aWithSymbolPath ) ->
FOOTPRINT*
2828 if( aWithSymbolPath )
2840 pad->SetNumber( wxT(
"1" ) );
2852 FOOTPRINT* refNC0 = makeReceptacle( wxT(
"J3" ), wxT(
"NC_0" ),
false );
2853 FOOTPRINT* refNC1 = makeReceptacle( wxT(
"J5" ), wxT(
"NC_1" ),
false );
2854 FOOTPRINT* refNO0 = makeReceptacle( wxT(
"J6" ), wxT(
"NO_0" ),
false );
2855 FOOTPRINT* refNO1 = makeReceptacle( wxT(
"J7" ), wxT(
"NO_1" ),
false );
2858 FOOTPRINT* tgtNO1 = makeReceptacle( wxT(
"J20" ), wxT(
"NO_1" ),
true );
2859 FOOTPRINT* tgtNO0 = makeReceptacle( wxT(
"J21" ), wxT(
"NO_0" ),
true );
2860 FOOTPRINT* tgtNC1 = makeReceptacle( wxT(
"J22" ), wxT(
"NC_1" ),
true );
2861 FOOTPRINT* tgtNC0 = makeReceptacle( wxT(
"J23" ), wxT(
"NC_0" ),
true );
2863 auto cgRef = std::make_unique<CONNECTION_GRAPH>();
2864 cgRef->AddFootprint( refNC0,
VECTOR2I( 0, 0 ) );
2865 cgRef->AddFootprint( refNC1,
VECTOR2I( 0, 0 ) );
2866 cgRef->AddFootprint( refNO0,
VECTOR2I( 0, 0 ) );
2867 cgRef->AddFootprint( refNO1,
VECTOR2I( 0, 0 ) );
2868 cgRef->BuildConnectivity();
2870 auto cgTarget = std::make_unique<CONNECTION_GRAPH>();
2871 cgTarget->AddFootprint( tgtNO1,
VECTOR2I( 0, 0 ) );
2872 cgTarget->AddFootprint( tgtNO0,
VECTOR2I( 0, 0 ) );
2873 cgTarget->AddFootprint( tgtNC1,
VECTOR2I( 0, 0 ) );
2874 cgTarget->AddFootprint( tgtNC0,
VECTOR2I( 0, 0 ) );
2875 cgTarget->BuildConnectivity();
2878 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
2879 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
2881 BOOST_CHECK( status );
2885 for(
const auto& [refFp, targetFp] :
result )
2887 BOOST_TEST_MESSAGE( wxString::Format(
"%s (%s) -> %s (%s)", refFp->GetReference(), refFp->GetValue(),
2888 targetFp->GetReference(), targetFp->GetValue() ) );
2901 m_board = std::make_unique<BOARD>();
2904 auto makeReceptacle =
2905 [&](
const wxString& aRef,
const wxString& aValue,
const VECTOR2I& aPos,
2909 fp->
SetFPID(
LIB_ID( wxT(
"Don-Con" ), wxT(
"Mill-Max-Pin_Receptacle" ) ) );
2914 if( aWithSymbolPath )
2922 m_board->Add( net );
2926 pad->SetNumber( wxT(
"1" ) );
2931 pad->SetPosition( aPos );
2939 [](
double a,
double b )
2948 return (
double) ( aNC1.x - aNC0.
x ) * ( aNO0.y - aNC0.
y ) - (double) ( aNC1.y - aNC0.
y ) * ( aNO0.x - aNC0.
x );
2952 FOOTPRINT* srcNC0 = makeReceptacle( wxT(
"J3" ), wxT(
"NC_0" ), mm( 0, 0 ),
false );
2953 FOOTPRINT* srcNC1 = makeReceptacle( wxT(
"J5" ), wxT(
"NC_1" ), mm( 0, 7.9 ),
false );
2954 FOOTPRINT* srcNO0 = makeReceptacle( wxT(
"J6" ), wxT(
"NO_0" ), mm( -3.58, 2.28 ),
false );
2955 FOOTPRINT* srcNO1 = makeReceptacle( wxT(
"J7" ), wxT(
"NO_1" ), mm( 3.58, 5.62 ),
false );
2960 FOOTPRINT* dstNC0 = makeReceptacle( wxT(
"J8" ), wxT(
"NC_0" ), mm( 40, 40 ),
true );
2961 FOOTPRINT* dstNC1 = makeReceptacle( wxT(
"J9" ), wxT(
"NC_1" ), mm( 40, 47.9 ),
true );
2962 FOOTPRINT* dstNO0 = makeReceptacle( wxT(
"J10" ), wxT(
"NO_0" ), mm( 43.58, 42.28 ),
true );
2963 FOOTPRINT* dstNO1 = makeReceptacle( wxT(
"J11" ), wxT(
"NO_1" ), mm( 36.42, 45.62 ),
true );
2968 destGroup->
SetName( wxT(
"Pin receptacles for safety switch" ) );
2970 for(
FOOTPRINT* fp : { dstNC0, dstNC1, dstNO0, dstNO1 } )
2973 m_board->Add( destGroup );
2978 for(
FOOTPRINT* fp : { srcNC0, srcNC1, srcNO0, srcNO1 } )
2992 for(
FOOTPRINT* fp : { dstNC0, dstNC1, dstNO0, dstNO1 } )
3002 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3008 .m_connectedRoutingOnly =
false,
3009 .m_copyPlacement =
true,
3010 .m_copyOtherItems =
false,
3011 .m_groupItems =
false,
3012 .m_includeLockedItems =
true,
3013 .m_anchorFp =
nullptr };
3016 BOOST_REQUIRE_MESSAGE(
result >= 0,
"RepeatLayout failed" );
3023 BOOST_TEST_MESSAGE( wxString::Format( wxT(
"block chirality %.0f, destination chirality after apply %.0f" ),
3024 srcChir, dstChir ) );
3026 BOOST_CHECK_MESSAGE( srcChir * dstChir > 0.0,
"Applied layout left the receptacles mirrored (NC/NO swapped)" );
3038 m_board = std::make_unique<BOARD>();
3042 [&](
const wxString& aName ) ->
int
3045 m_board->Add( net );
3049 const int netGnd = addNet( wxT(
"GND" ) );
3050 const int netVcc = addNet( wxT(
"+3V3" ) );
3052 const LIB_ID icId( wxT(
"TestLib" ), wxT(
"SOT-23-6" ) );
3053 const LIB_ID rId( wxT(
"TestLib" ), wxT(
"R_0402" ) );
3058 [&](
int aIdx,
int aStrapNet ) ->
ZONE*
3060 const int netOut = addNet( wxString::Format( wxT(
"Net-(U%d-OUT)" ), aIdx ) );
3061 const int netCfg = addNet( wxString::Format( wxT(
"Net-(U%d-CFG)" ), aIdx ) );
3064 [&](
FOOTPRINT* aFp,
const wxString& aNumber,
int aNetCode )
3068 pad->SetNumber( aNumber );
3073 pad->SetNetCode( aNetCode );
3079 ic->
SetReference( wxString::Format( wxT(
"U%d" ), aIdx ) );
3081 addPad( ic, wxT(
"1" ), netOut );
3082 addPad( ic, wxT(
"2" ), netGnd );
3086 r->
SetReference( wxString::Format( wxT(
"R%d" ), aIdx ) );
3088 addPad( r, wxT(
"1" ), netCfg );
3089 addPad( r, wxT(
"2" ), aStrapNet );
3091 ZONE* zone =
new ZONE( m_board.get() );
3092 m_board->Add( zone );
3097 std::vector<std::pair<int, int>> channelStraps = { { 1, netGnd }, { 2, netGnd }, { 3, netVcc } };
3098 std::vector<ZONE*> zones;
3102 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3108 ruleData->
m_areas.reserve( channelStraps.size() );
3110 for(
const auto& [idx, strap] : channelStraps )
3112 ZONE* zone = makeChannel( idx, strap );
3113 zones.push_back( zone );
3117 ra.
m_ruleName = wxString::Format( wxT(
"Channel %d" ), idx );
3119 for(
FOOTPRINT* fp : m_board->Footprints() )
3121 if( fp->GetReference() == wxString::Format( wxT(
"U%d" ), idx )
3122 || fp->GetReference() == wxString::Format( wxT(
"R%d" ), idx ) )
3128 ruleData->
m_areas.push_back( ra );
3133 BOOST_REQUIRE_EQUAL( status, 0 );
3137 if( ra.
m_zone == zones[1] )
3143 for(
const wxString& reason : it->second.m_mismatchReasons )
3146 BOOST_CHECK_MESSAGE( it->second.m_isOk,
3147 wxString::Format(
"Channel 2 must match %s", ra.
m_ruleName ) );
3159 m_board = std::make_unique<BOARD>();
3163 [&](
const wxString& aName ) ->
int
3166 m_board->Add( net );
3170 const int netGnd = addNet( wxT(
"GND" ) );
3172 const LIB_ID icId( wxT(
"TestLib" ), wxT(
"SOT-23-6" ) );
3173 const LIB_ID rId( wxT(
"TestLib" ), wxT(
"R_0402" ) );
3176 [&](
FOOTPRINT* aFp,
const wxString& aNumber,
int aNetCode )
3180 pad->SetNumber( aNumber );
3184 pad->SetNetCode( aNetCode );
3199 const int netLocal1 = addNet( wxT(
"Net-(U1-SIG)" ) );
3200 FOOTPRINT* u1 = makeFp( icId, wxT(
"U1" ) );
3201 addPad( u1, wxT(
"1" ), netLocal1 );
3202 addPad( u1, wxT(
"2" ), netGnd );
3203 FOOTPRINT* r1 = makeFp( rId, wxT(
"R1" ) );
3204 addPad( r1, wxT(
"1" ), netLocal1 );
3205 addPad( r1, wxT(
"2" ), netGnd );
3209 const int netLocal2a = addNet( wxT(
"Net-(U2-SIG)" ) );
3210 const int netLocal2b = addNet( wxT(
"Net-(R2-SIG)" ) );
3211 FOOTPRINT* u2 = makeFp( icId, wxT(
"U2" ) );
3212 addPad( u2, wxT(
"1" ), netLocal2a );
3213 addPad( u2, wxT(
"2" ), netGnd );
3214 FOOTPRINT* r2 = makeFp( rId, wxT(
"R2" ) );
3215 addPad( r2, wxT(
"1" ), netLocal2b );
3216 addPad( r2, wxT(
"2" ), netGnd );
3218 ZONE* zone1 =
new ZONE( m_board.get() );
3219 m_board->Add( zone1 );
3220 ZONE* zone2 =
new ZONE( m_board.get() );
3221 m_board->Add( zone2 );
3222 ZONE* zoneDup =
new ZONE( m_board.get() );
3223 m_board->Add( zoneDup );
3227 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3233 ruleData->
m_areas.reserve( 3 );
3239 ruleData->
m_areas.push_back( ra1 );
3245 ruleData->
m_areas.push_back( ra2 );
3251 raDup.
m_ruleName = wxT(
"Channel 1 duplicate" );
3253 ruleData->
m_areas.push_back( raDup );
3256 BOOST_REQUIRE_EQUAL( status, 0 );
3271 BOOST_CHECK_MESSAGE( !it->second.m_isOk,
3272 "Channel 1 and channel 2 differ on a local net and must not match; an "
3273 "overlapping rule area must not hide that net by marking it global" );
3285 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3320 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3351 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3358 auto ruleData = mtTool->
GetData();
3361 for(
RULE_AREA& ra : ruleData->m_areas )
3381 std::vector<PCB_GENERATOR*> strip;
3385 if( gen->GetGeneratorType() != wxT(
"tuning_pattern" ) )
3389 strip.push_back( gen );
3394 for(
EDA_ITEM* member : gen->GetItems() )
3396 if( member->IsBOARD_ITEM() )
3397 static_cast<BOARD_ITEM*
>( member )->SetParentGroup(
nullptr );
3400 m_board->Remove( gen );
3407 ruleData->m_options.m_groupItems =
true;
3410 ruleData->m_compatMap[target].m_doCopy =
true;
3427 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3434 auto ruleData = mtTool->
GetData();
3437 for(
RULE_AREA& ra : ruleData->m_areas )
3453 std::set<EDA_GROUP*> groups;
3465 for(
EDA_ITEM* member : g->GetItems() )
3489 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3496 auto ruleData = mtTool->
GetData();
3499 for(
RULE_AREA& ra : ruleData->m_areas )
3523 std::vector<ZONE*> stripZones;
3525 for(
ZONE* zone : m_board->Zones() )
3527 if( zone->GetIsRuleArea() )
3531 stripZones.push_back( zone );
3534 for(
ZONE* zone : stripZones )
3537 group->RemoveItem( zone );
3539 m_board->Remove( zone );
3545 ruleData->m_compatMap[target].m_doCopy =
true;
3563 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3580 std::vector<TMATCH::TOPOLOGY_MISMATCH_REASON> details;
3581 bool status = cgRef->FindIsomorphism( cgTarget.get(),
result, details );
3583 BOOST_CHECK( status );
3584 BOOST_CHECK( details.empty() );
3596 m_board = std::make_unique<BOARD>();
3597 BOARD* board = m_board.get();
3602 [&](
const wxString& aRef,
const VECTOR2I& aPos )
3612 pad->SetNumber( wxT(
"1" ) );
3621 pad->SetNetCode( 1 );
3633 board->
Add( track );
3638 toolMgr.
SetEnvironment( board,
nullptr,
nullptr,
nullptr, toolsHolder );
3645 auto ruleData = mtTool->
GetData();
3655 ZONE* raZone =
nullptr;
3659 if( zone->GetIsRuleArea() && zone->GetZoneName() == wxT(
"auto-placement-area-/ChannelA/" ) )
3672 "Sheet rule area outline must enclose loose inter-block routing (issue 24983)" );
3688 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr, toolsHolder );
3695 auto ruleData = mtTool->
GetData();
3700 for(
RULE_AREA& ra : ruleData->m_areas )
3702 if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet/" ) ) )
3704 else if( ra.
m_ruleName.Contains( wxT(
"Untitled Sheet1/" ) ) )
3740 ruleData->m_compatMap[targetArea].m_doCopy =
true;
3741 ruleData->m_options.m_copyPlacement =
true;
constexpr EDA_IU_SCALE pcbIUScale
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
virtual void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
void SetLocked(bool aLocked) override
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
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.
const GENERATORS & Generators() const
const ZONES & Zones() const
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
constexpr const Vec GetEnd() const
constexpr const Vec & GetOrigin() const
Store all of the related component information found in a netlist.
A set of EDA_ITEMs (i.e., without duplicates).
void AddItem(EDA_ITEM *aItem)
Add item to group.
virtual EDA_ITEM * AsEdaItem()=0
void SetName(const wxString &aName)
A base class for most all the KiCad significant classes used in schematics and boards.
virtual EDA_GROUP * GetParentGroup() const
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
virtual bool IsVisible() const
virtual void SetText(const wxString &aText)
A logical library item identifier and consists of various portions much like a URI.
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Handle the data for a net.
@ NORMAL
Shape is the same on all layers.
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
void SetLocalZoneConnection(ZONE_CONNECTION aType)
std::optional< int > GetLocalSolderMaskMargin() const
ZONE_CONNECTION GetLocalZoneConnection() const
A set of BOARD_ITEMs (i.e., without duplicates).
void SetEnd(const VECTOR2I &aEnd) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetStart(const VECTOR2I &aStart) override
void SetStroke(const STROKE_PARAMS &aStroke) override
EDA_ANGLE GetTextAngle() const override
void SetPosition(const VECTOR2I &aPos) override
void SetEnd(const VECTOR2I &aEnd)
void SetStart(const VECTOR2I &aStart)
const VECTOR2I & GetStart() const
virtual void SetWidth(int aWidth)
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Simple container to manage line stroke parameters.
Handle a list of polygons defining a copper zone.
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
wxString GetPlacementAreaSource() const
SHAPE_POLY_SET * Outline()
void SetIsRuleArea(bool aEnable)
void SetLayerSet(const LSET &aLayerSet) override
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
PCB_LAYER_ID
A quick note on layer IDs:
SHAPE_LINE_CHAIN BoxToLineChain(const BOX2I &aBox)
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
std::map< FOOTPRINT *, FOOTPRINT * > COMPONENT_MATCHES
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ SMD
Smd pad, appears on the solder paste layer (default)
Class to handle a set of BOARD_ITEMs.
Utility functions for working with shapes.
std::vector< FAB_LAYER_COLOR > dummy
MULTICHANNEL_TEST_FIXTURE()
SETTINGS_MANAGER m_settingsManager
std::unique_ptr< BOARD > m_board
bool m_includeLockedItems
std::unordered_map< RULE_AREA *, RULE_AREA_COMPAT_DATA > m_compatMap
std::vector< RULE_AREA > m_areas
std::unordered_set< EDA_ITEM * > m_designBlockItems
PLACEMENT_SOURCE_T m_sourceType
std::set< FOOTPRINT * > m_components
std::atomic< bool > * m_cancelled
std::atomic< int > * m_matchedComponents
std::atomic< int > * m_totalComponents
@ VALUE
Field Value of part, i.e. "3.3K".
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
static FOOTPRINT * addFootprint(BOARD &aBoard, const wxString &aReference)
VECTOR3I expected(15, 30, 45)
int countCopperZonesInArea(BOARD *aBoard, ZONE *aRAZone)
RULE_AREA * findGroupRuleAreaByName(MULTICHANNEL_TOOL *aTool, const wxString &aGroupName)
RULE_AREA * findRuleAreaByPartialName(MULTICHANNEL_TOOL *aTool, const wxString &aName)
int countZonesByNamePrefixInRuleArea(BOARD *aBoard, const wxString &aBaseName, const RULE_AREA &aRuleArea)
RULE_AREA * findSheetRuleAreaByPath(MULTICHANNEL_TOOL *aTool, wxString aPath)
BOOST_FIXTURE_TEST_CASE(MultichannelToolRegressions, MULTICHANNEL_TEST_FIXTURE)
int countZonesByNameInRuleArea(BOARD *aBoard, const wxString &aZoneName, const RULE_AREA &aRuleArea)
int countTuningGensInArea(BOARD *aBoard, ZONE *aZone)
RULE_AREA * findRuleAreaByPlacementGroup(MULTICHANNEL_TOOL *aTool, const wxString &aGroupName)
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ 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_TEXT_T
class PCB_TEXT, text on a layer
VECTOR2< int32_t > VECTOR2I
@ NONE
Pads are not covered.