21#include <boost/test/data/test_case.hpp>
51 double aMinArea,
const wxString& aLabel )
53 for(
int ii = 0; ii < aFill->OutlineCount(); ++ii )
55 const double area =
std::abs( aFill->Outline( ii ).Area() );
57 BOOST_CHECK_MESSAGE( area >= aMinArea,
58 wxString::Format(
"%s %d area %.0f IU^2 below %.0f IU^2; partial "
59 "stamps should not survive.",
60 aLabel, ii, area, aMinArea ) );
90 for(
PAD*
pad : m_board->Footprints()[0]->Pads() )
92 if(
pad->GetNumber() ==
"2" ||
pad->GetNumber() ==
"4" ||
pad->GetNumber() ==
"6" )
103 for(
PCB_TRACK* track : m_board->Tracks() )
111 arc8 = track->m_Uuid;
112 track->SetWidth( track->GetWidth() +
delta +
delta );
116 arc12 = track->m_Uuid;
122 bool foundPad2Error =
false;
123 bool foundPad4Error =
false;
124 bool foundPad6Error =
false;
125 bool foundArc8Error =
false;
126 bool foundArc12Error =
false;
127 bool foundOtherError =
false;
132 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
133 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
137 BOARD_ITEM* item_a = m_board->ResolveItem( aItem->GetMainItemID() );
138 PAD* pad_a =
dynamic_cast<PAD*
>( item_a );
141 BOARD_ITEM* item_b = m_board->ResolveItem( aItem->GetAuxItemID() );
142 PAD* pad_b =
dynamic_cast<PAD*
>( item_b );
145 if( pad_a && pad_a->
GetNumber() ==
"2" ) foundPad2Error =
true;
146 else if( pad_a && pad_a->
GetNumber() ==
"4" ) foundPad4Error =
true;
147 else if( pad_a && pad_a->
GetNumber() ==
"6" ) foundPad6Error =
true;
148 else if( pad_b && pad_b->
GetNumber() ==
"2" ) foundPad2Error =
true;
149 else if( pad_b && pad_b->
GetNumber() ==
"4" ) foundPad4Error =
true;
150 else if( pad_b && pad_b->
GetNumber() ==
"6" ) foundPad6Error =
true;
151 else if( trk_a && trk_a->
m_Uuid == arc8 ) foundArc8Error =
true;
152 else if( trk_a && trk_a->
m_Uuid == arc12 ) foundArc12Error =
true;
153 else if( trk_b && trk_b->
m_Uuid == arc8 ) foundArc8Error =
true;
154 else if( trk_b && trk_b->
m_Uuid == arc12 ) foundArc12Error =
true;
155 else foundOtherError =
true;
185 for(
ZONE* zone : m_board->Zones() )
187 if( zone->GetLayerSet().Contains(
F_Cu ) )
224 std::vector<DRC_ITEM> violations;
227 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
228 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
231 violations.push_back( *aItem );
236 if( violations.empty() )
245 std::map<KIID, EDA_ITEM*> itemMap;
246 m_board->FillItemMap( itemMap );
248 for(
const DRC_ITEM& item : violations )
251 BOOST_ERROR( wxString::Format(
"Zone fill regression: %s failed", relPath ) );
270 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
273 auto runDrcClearanceCheck =
274 [
this](
bool aIterative ) ->
int
285 std::vector<DRC_ITEM> violations;
287 std::map<KIID, EDA_ITEM*> itemMap;
288 m_board->FillItemMap( itemMap );
292 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
294 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
298 BOARD_ITEM* itemA = m_board->ResolveItem( aItem->GetMainItemID() );
299 BOARD_ITEM* itemB = m_board->ResolveItem( aItem->GetAuxItemID() );
301 if(
dynamic_cast<ZONE*
>( itemA ) &&
dynamic_cast<ZONE*
>( itemB ) )
303 violations.push_back( *aItem );
306 aItem->ShowReport( &unitsProvider,
314 return static_cast<int>( violations.size() );
317 int iterativeViolations = runDrcClearanceCheck(
true );
319 BOOST_CHECK_MESSAGE( iterativeViolations == 0,
320 wxString::Format(
"Iterative refill produced %d zone-to-zone clearance "
321 "violations (expected 0)", iterativeViolations ) );
323 int nonIterativeViolations = runDrcClearanceCheck(
false );
325 BOOST_CHECK_MESSAGE( nonIterativeViolations == 0,
326 wxString::Format(
"Non-iterative refill produced %d zone-to-zone clearance "
327 "violations (expected 0)", nonIterativeViolations ) );
346 std::vector<DRC_ITEM> violations;
349 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
350 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
353 violations.push_back( *aItem );
358 if( violations.empty() )
361 BOOST_TEST_MESSAGE( wxString::Format(
"Zone fill copper sliver regression: %s passed", relPath ) );
367 std::map<KIID, EDA_ITEM*> itemMap;
368 m_board->FillItemMap( itemMap );
370 for(
const DRC_ITEM& item : violations )
373 BOOST_ERROR( wxString::Format(
"Zone fill copper sliver regression: %s failed", relPath ) );
379 {
"teardrop_issue_JPC2", 5 },
386 const wxString& relPath =
test.first;
387 const int count =
test.second;
397 for(
ZONE* zone : m_board->Zones() )
399 if( zone->IsTeardropArea() )
403 BOOST_CHECK_MESSAGE( zoneCount == count,
"Expected " << count <<
" teardrop zones in "
404 << relPath <<
", found "
412 std::vector<wxString> tests = { {
"issue19956/issue19956" }
415 for(
const wxString& relPath : tests )
421 for(
ZONE* zone : m_board->Zones() )
425 std::shared_ptr<SHAPE> a_shape( zone->GetEffectiveShape( layer ) );
427 for(
PAD*
pad : m_board->GetPads() )
429 std::shared_ptr<SHAPE> pad_shape(
pad->GetEffectiveShape( layer ) );
430 int clearance = pad_shape->GetClearance( a_shape.get() );
431 BOOST_CHECK_MESSAGE(
pad->GetNetCode() == zone->GetNetCode() ||
clearance != 0,
432 wxString::Format(
"Pad %s from Footprint %s has net code %s and "
433 "is connected to zone with net code %s",
435 pad->GetParentFootprint()->GetReferenceAsString(),
437 zone->GetNetname() ) );
467 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } } guard{ cfg.
m_ZoneFillIterativeRefill, originalIterativeRefill };
474 ZONE* gndZone =
nullptr;
476 for(
ZONE* zone : m_board->Zones() )
478 if( zone->GetNetname() ==
"GND" )
485 BOOST_REQUIRE_MESSAGE( gndZone !=
nullptr,
"GND zone not found in test board" );
489 bool hasOutline = m_board->GetBoardPolygonOutlines( boardOutline,
true );
490 BOOST_REQUIRE_MESSAGE( hasOutline,
"Board outline not found" );
492 double boardArea = 0.0;
503 double fillRatio = gndFilledArea / boardArea;
505 BOOST_TEST_MESSAGE( wxString::Format(
"Board area: %.2f sq mm, GND filled area: %.2f sq mm, "
506 "Fill ratio: %.1f%%",
507 boardArea / 1e6, gndFilledArea / 1e6,
508 fillRatio * 100.0 ) );
510 BOOST_CHECK_MESSAGE( fillRatio >= 0.25,
511 wxString::Format(
"GND zone fill ratio %.1f%% is less than expected 25%%. "
512 "This indicates issue 21746 - lower priority zones not "
513 "filling areas where higher priority isolated islands "
515 fillRatio * 100.0 ) );
540 int viasWithUnreachableFlashing = 0;
541 int totalConditionalVias = 0;
543 PCB_LAYER_ID in1Cu = m_board->GetLayerID( wxT(
"In1.Cu" ) );
544 PCB_LAYER_ID in2Cu = m_board->GetLayerID( wxT(
"In2.Cu" ) );
546 for(
PCB_TRACK* track : m_board->Tracks() )
553 if( !
via->GetRemoveUnconnected() )
556 totalConditionalVias++;
559 bool flashedOnIn1 =
via->FlashLayer( in1Cu );
560 bool flashedOnIn2 =
via->FlashLayer( in2Cu );
562 if( !flashedOnIn1 && !flashedOnIn2 )
566 int holeRadius =
via->GetDrillValue() / 2;
569 bool zoneReachesVia =
false;
571 for(
ZONE* zone : m_board->Zones() )
573 if( zone->GetIsRuleArea() )
576 if( zone->GetNetCode() !=
via->GetNetCode() )
581 if( !zone->IsOnLayer( layer ) )
584 if( !zone->HasFilledPolysForLayer( layer ) )
587 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
589 if( fill->Contains( viaCenter, -1, holeRadius ) )
591 zoneReachesVia =
true;
601 if( !zoneReachesVia && ( flashedOnIn1 || flashedOnIn2 ) )
602 viasWithUnreachableFlashing++;
605 BOOST_TEST_MESSAGE( wxString::Format(
"Total conditional vias: %d, Vias with unreachable "
606 "flashing: %d", totalConditionalVias,
607 viasWithUnreachableFlashing ) );
609 BOOST_CHECK_MESSAGE( viasWithUnreachableFlashing == 0,
610 wxString::Format(
"Found %d vias flashed on zone layers where the zone "
611 "fill doesn't actually reach them. This indicates "
612 "issue 22010 is not fixed.",
613 viasWithUnreachableFlashing ) );
635 int viasShortingZones = 0;
636 int totalConditionalVias = 0;
638 for(
PCB_TRACK* track : m_board->Tracks() )
645 if( !
via->GetRemoveUnconnected() )
648 totalConditionalVias++;
652 for(
ZONE* zone : m_board->Zones() )
654 if( zone->GetIsRuleArea() )
657 if( zone->GetNetCode() ==
via->GetNetCode() )
662 if( !
via->FlashLayer( layer ) )
665 if( !zone->HasFilledPolysForLayer( layer ) )
668 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
669 int viaRadius =
via->GetWidth( layer ) / 2;
671 if( fill->Contains( viaCenter, -1, viaRadius ) )
674 "Via at (%d, %d) on net %s is flashing on layer %s where zone "
675 "net %s is filled - this creates a short!",
676 viaCenter.
x, viaCenter.
y,
via->GetNetname(),
677 m_board->GetLayerName( layer ), zone->GetNetname() ) );
684 BOOST_TEST_MESSAGE( wxString::Format(
"Total conditional vias: %d, Vias shorting zones: %d",
685 totalConditionalVias, viasShortingZones ) );
687 BOOST_CHECK_MESSAGE( viasShortingZones == 0,
688 wxString::Format(
"Found %d vias flashed on layers where they short to "
689 "zones with different nets. This indicates issue 12964 "
691 viasShortingZones ) );
709 KI_TEST::LoadBoard( m_settingsManager,
"hatch_thermal_connectivity/hatch_thermal_connectivity",
714 m_board->BuildConnectivity();
716 int unconnectedCount = m_board->GetConnectivity()->GetUnconnectedCount(
false );
718 BOOST_CHECK_MESSAGE( unconnectedCount == 0,
719 wxString::Format(
"Found %d unconnected items after zone fill. "
720 "Hatch zone thermal reliefs should maintain connectivity "
721 "even with large hatch gaps.",
722 unconnectedCount ) );
746 PCB_LAYER_ID in1Cu = m_board->GetLayerID( wxT(
"In1.Cu" ) );
748 ZONE* gndZone =
nullptr;
750 for(
ZONE* zone : m_board->Zones() )
752 if( zone->GetNetname() ==
"GND" && zone->IsOnLayer( in1Cu ) )
759 BOOST_REQUIRE_MESSAGE( gndZone !=
nullptr,
"GND zone on In1.Cu not found in test board" );
767 "GND zone has no fill on In1.Cu" );
775 double zoneOutlineArea = gndZone->
Outline()->
Area();
777 BOOST_REQUIRE_MESSAGE( zoneOutlineArea > 0.0,
"Zone outline area must be positive" );
779 double fillArea = 0.0;
781 for(
int i = 0; i < fill->OutlineCount(); i++ )
782 fillArea +=
std::abs( fill->Outline( i ).Area() );
784 double fillRatio = fillArea / zoneOutlineArea;
788 BOOST_CHECK_GE( fillRatio, 0.90 );
811 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
819 std::vector<ZONE*> keepouts;
821 for(
ZONE* zone : m_board->Zones() )
823 if( zone->GetIsRuleArea() && zone->GetDoNotAllowZoneFills() )
824 keepouts.push_back( zone );
827 BOOST_REQUIRE_MESSAGE( !keepouts.empty(),
"No zone keepouts found in test board" );
830 int violationCount = 0;
832 for(
ZONE* keepout : keepouts )
834 for(
PCB_LAYER_ID layer : keepout->GetLayerSet().Seq() )
839 for(
ZONE* zone : m_board->Zones() )
841 if( zone->GetIsRuleArea() )
844 if( !zone->IsOnLayer( layer ) )
847 if( !zone->HasFilledPolysForLayer( layer ) )
850 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
858 double intersectionArea = 0;
864 if( intersectionArea > 1e6 )
867 "Zone %s fill on layer %s overlaps keepout by %.2f sq mm",
869 m_board->GetLayerName( layer ),
870 intersectionArea / 1e6 ) );
878 BOOST_CHECK_MESSAGE( violationCount == 0,
879 wxString::Format(
"Found %d zone fills overlapping keepout areas. "
880 "This indicates issue 22809 - iterative refiller "
881 "ignores zone keepouts.", violationCount ) );
904 PCB_LAYER_ID in2Cu = m_board->GetLayerID( wxT(
"In2.Cu" ) );
905 int padsWithMissingFlashing = 0;
906 int totalConditionalPads = 0;
908 for(
FOOTPRINT* footprint : m_board->Footprints() )
910 for(
PAD*
pad : footprint->Pads() )
912 if( !
pad->GetRemoveUnconnected() )
915 if( !
pad->HasHole() )
918 if(
pad->GetNetname() !=
"VBUS_DUT" &&
pad->GetNetname() !=
"VBUS_DBG" )
921 totalConditionalPads++;
924 bool shouldFlash =
false;
926 for(
ZONE* zone : m_board->Zones() )
928 if( zone->GetIsRuleArea() )
931 if( zone->GetNetCode() !=
pad->GetNetCode() )
934 if( !zone->IsOnLayer( in2Cu ) )
937 if( zone->Outline()->Contains(
pad->GetPosition() ) )
944 if( shouldFlash && !
pad->FlashLayer( in2Cu ) )
947 "Pad %s at (%d, %d) on net %s is inside zone but not flashing on In2.Cu",
948 pad->GetNumber(),
pad->GetPosition().x,
pad->GetPosition().y,
949 pad->GetNetname() ) );
950 padsWithMissingFlashing++;
955 BOOST_TEST_MESSAGE( wxString::Format(
"Total conditional pads: %d, Pads with missing "
956 "flashing: %d", totalConditionalPads,
957 padsWithMissingFlashing ) );
959 BOOST_CHECK_MESSAGE( padsWithMissingFlashing == 0,
960 wxString::Format(
"Found %d TH pads that should flash on inner layers "
961 "but don't. This indicates issue 22826 is not fixed.",
962 padsWithMissingFlashing ) );
981 const PCB_LAYER_ID innerLayers[] = { m_board->GetLayerID( wxT(
"In1.Cu" ) ),
982 m_board->GetLayerID( wxT(
"In2.Cu" ) ) };
984 int padsWithMissingFlashing = 0;
985 int totalConditionalPads = 0;
987 for(
FOOTPRINT* footprint : m_board->Footprints() )
989 for(
PAD*
pad : footprint->Pads() )
991 if( !
pad->GetRemoveUnconnected() || !
pad->HasHole() )
996 bool shouldFlash =
false;
998 for(
ZONE* zone : m_board->Zones() )
1000 if( zone->GetIsRuleArea() || zone->GetNetCode() !=
pad->GetNetCode() )
1003 if( !zone->IsOnLayer( layer ) )
1006 if( zone->Outline()->Contains(
pad->GetPosition() ) )
1016 totalConditionalPads++;
1018 if( !
pad->FlashLayer( layer ) )
1021 wxString::Format(
"Pad %s (drill %.2f mm, spoke angle %.0f deg) on net %s is inside the "
1022 "zone but not flashing on %s",
1024 pad->GetThermalSpokeAngle().AsDegrees(),
pad->GetNetname(),
1025 m_board->GetLayerName( layer ) ) );
1026 padsWithMissingFlashing++;
1032 BOOST_TEST_MESSAGE( wxString::Format(
"Pads inside inner-layer zones: %d, missing flashing: %d",
1033 totalConditionalPads, padsWithMissingFlashing ) );
1035 BOOST_CHECK_MESSAGE( padsWithMissingFlashing == 0,
1036 wxString::Format(
"Found %d TH pads inside a same-net inner-layer zone "
1037 "that lost their annular ring after fill. This "
1038 "indicates issue 24865 is not fixed.",
1039 padsWithMissingFlashing ) );
1057 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1067 if( !commit.
Empty() )
1071 int teardropCount = 0;
1072 bool foundBadTeardrop =
false;
1074 for(
ZONE* zone : m_board->Zones() )
1076 if( !zone->IsTeardropArea() )
1093 int concaveCount = 0;
1095 for(
int i = 0; i <
chain.PointCount(); i++ )
1097 int prev = ( i == 0 ) ?
chain.PointCount() - 1 : i - 1;
1098 int next = ( i + 1 ) %
chain.PointCount();
1104 int64_t cross = (int64_t)
v1.x *
v2.y - (int64_t)
v1.y *
v2.x;
1114 if( concaveCount > 5 )
1117 "indicating possible corner intersection",
1119 foundBadTeardrop =
true;
1123 BOOST_CHECK_MESSAGE( teardropCount > 0,
"Expected at least one teardrop zone" );
1125 BOOST_CHECK_MESSAGE( !foundBadTeardrop,
1126 "Found teardrop with excessive concave vertices, indicating "
1127 "issue 19405 - teardrop curve intersecting rounded rectangle corner" );
1141 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1150 if( !commit.
Empty() )
1153 int teardropCount = 0;
1154 bool foundSpike =
false;
1156 const int maxError = m_board->GetDesignSettings().m_MaxError;
1158 for(
ZONE* zone : m_board->Zones() )
1160 if( !zone->IsTeardropArea() )
1166 int netcode = zone->GetNetCode();
1174 for(
FOOTPRINT* fp : m_board->Footprints() )
1176 for(
PAD*
pad : fp->Pads() )
1178 if(
pad->GetNetCode() == netcode &&
pad->IsOnLayer( layer ) )
1179 pad->TransformShapeToPolygon( corridor, layer, 0, maxError,
ERROR_OUTSIDE );
1183 for(
PCB_TRACK* track : m_board->Tracks() )
1185 if( track->GetNetCode() == netcode && track->IsOnLayer( layer ) )
1186 track->TransformShapeToPolygon( corridor, layer, 0, maxError,
ERROR_OUTSIDE );
1198 double tdArea =
std::abs( zone->Outline()->Area() );
1200 double ratio = tdArea > 0 ? outArea / tdArea : 0.0;
1203 "Teardrop on layer %d: area %.0f, area outside corridor %.0f (%.1f%%)",
1204 (
int) layer, tdArea, outArea, ratio * 100.0 ) );
1210 "Teardrop on layer %d sweeps %.1f%% of its area outside the track/pad "
1212 (
int) layer, ratio * 100.0 ) );
1216 BOOST_CHECK_MESSAGE( teardropCount > 0,
"Expected at least one teardrop zone" );
1217 BOOST_CHECK_MESSAGE( !foundSpike,
1218 "A teardrop vertex spikes outside the track/pad corridor it should "
1236 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1245 if( !commit.
Empty() )
1252 for(
FOOTPRINT* fp : m_board->Footprints() )
1254 for(
PAD*
pad : fp->Pads() )
1259 maxReach = std::max( maxReach, prms.
m_TdMaxLen + std::min( size.
x, size.
y ) );
1265 int teardropCount = 0;
1266 bool foundSpike =
false;
1268 for(
ZONE* zone : m_board->Zones() )
1270 if( !zone->IsTeardropArea() )
1277 for(
const VECTOR2I& pt : zone->Outline()->Outline( 0 ).CPoints() )
1279 SEG::ecoord bestSq = std::numeric_limits<SEG::ecoord>::max();
1281 for(
PCB_TRACK* track : m_board->Tracks() )
1283 if( !track->IsOnLayer( layer ) )
1286 bestSq = std::min( bestSq,
1290 double dist = std::sqrt( (
double) bestSq );
1292 if( dist > maxReach )
1296 "Teardrop vertex (%.4f, %.4f) is %.4f mm from the nearest track, "
1297 "max allowed %.4f mm",
1307 BOOST_CHECK_MESSAGE( teardropCount == 2,
1308 wxString::Format(
"Expected a teardrop on each of the two pads covering "
1309 "the track end, found %d",
1311 BOOST_CHECK_MESSAGE( !foundSpike,
1312 "A teardrop reaches far past the track it anchors on, indicating it was "
1313 "built around the custom pad's anchor position instead of the copper the "
1331 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1341 if( !commit.
Empty() )
1345 int teardropCount = 0;
1346 bool foundBadTeardrop =
false;
1348 for(
ZONE* zone : m_board->Zones() )
1350 if( !zone->IsTeardropArea() )
1364 int concaveCount = 0;
1366 for(
int i = 0; i <
chain.PointCount(); i++ )
1368 int prev = ( i == 0 ) ?
chain.PointCount() - 1 : i - 1;
1369 int next = ( i + 1 ) %
chain.PointCount();
1374 int64_t cross = (int64_t)
v1.x *
v2.y - (int64_t)
v1.y *
v2.x;
1380 if( concaveCount > 5 )
1384 foundBadTeardrop =
true;
1388 BOOST_CHECK_MESSAGE( teardropCount > 0,
"Expected at least one teardrop zone" );
1390 BOOST_CHECK_MESSAGE( !foundBadTeardrop,
1391 "Found teardrop with excessive concave vertices on oval pad, "
1392 "indicating curve is not tangent to semicircular end" );
1409 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1419 if( !commit.
Empty() )
1423 PAD* largePad =
nullptr;
1425 for(
FOOTPRINT* fp : m_board->Footprints() )
1427 for(
PAD*
pad : fp->Pads() )
1437 BOOST_REQUIRE_MESSAGE( largePad !=
nullptr,
"Expected a circular pad in test board" );
1443 int teardropCount = 0;
1444 bool foundBadTeardrop =
false;
1446 for(
ZONE* zone : m_board->Zones() )
1448 if( !zone->IsTeardropArea() )
1461 int concaveCount = 0;
1463 for(
int i = 0; i <
chain.PointCount(); i++ )
1465 int prev = ( i == 0 ) ?
chain.PointCount() - 1 : i - 1;
1466 int next = ( i + 1 ) %
chain.PointCount();
1471 int64_t cross = (int64_t)
v1.x *
v2.y - (int64_t)
v1.y *
v2.x;
1477 if( concaveCount > 5 )
1479 BOOST_TEST_MESSAGE( wxString::Format(
"Large circle teardrop has %d concave vertices",
1481 foundBadTeardrop =
true;
1486 int maxError = m_board->GetDesignSettings().m_MaxError;
1488 for(
int i = 0; i <
chain.PointCount(); i++ )
1491 double dist = ( pt - padCenter ).EuclideanNorm();
1494 if( dist > padRadius * 0.5 && dist < padRadius * 1.5 )
1496 double deviation =
std::abs( dist - padRadius );
1499 if( deviation > maxError * 5 && deviation < padRadius * 0.2 )
1502 "Teardrop point at distance %.2f from pad center (radius %.2f), "
1503 "deviation %.2f exceeds tolerance",
1504 dist / 1000.0, padRadius / 1000.0, deviation / 1000.0 ) );
1510 BOOST_CHECK_MESSAGE( teardropCount > 0,
"Expected at least one teardrop zone" );
1512 BOOST_CHECK_MESSAGE( !foundBadTeardrop,
1513 "Found teardrop with excessive concave vertices on large circle, "
1514 "indicating anchor points may not be on circle edge" );
1538 for(
ZONE* zone : m_board->Zones() )
1540 if( zone->GetIsRuleArea() )
1545 if( !zone->HasFilledPolysForLayer( layer ) )
1548 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1550 for(
PAD*
pad : m_board->GetPads() )
1552 if( !
pad->IsOnLayer( layer ) )
1555 if(
pad->GetNetCode() == zone->GetNetCode() )
1558 std::shared_ptr<SHAPE> padShape =
pad->GetEffectiveShape( layer );
1559 int clearance = padShape->GetClearance( fill.get() );
1564 "Pad %s (net %s) at (%d, %d) has zero clearance to zone %s "
1566 pad->GetNumber(),
pad->GetNetname(),
1567 pad->GetPosition().x,
pad->GetPosition().y,
1568 zone->GetNetname(), m_board->GetLayerName( layer ) ) );
1575 BOOST_CHECK_MESSAGE( violations == 0,
1576 wxString::Format(
"Found %d pads with missing zone clearance. "
1577 "Coincident pads with different nets must not be "
1578 "deduplicated in zone fill knockout.",
1600 std::vector<DRC_ITEM> violations;
1605 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
1606 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
1610 BOARD_ITEM* item_a = m_board->ResolveItem( aItem->GetMainItemID() );
1611 BOARD_ITEM* item_b = m_board->ResolveItem( aItem->GetAuxItemID() );
1613 ZONE* zone_a =
dynamic_cast<ZONE*
>( item_a );
1614 ZONE* zone_b =
dynamic_cast<ZONE*
>( item_b );
1616 if( zone_a || zone_b )
1617 violations.push_back( *aItem );
1644 ZONE* hvZone =
nullptr;
1645 ZONE* lvZone =
nullptr;
1647 for(
ZONE* zone : m_board->Zones() )
1649 if( zone->GetNetname() ==
"HV_NET" )
1651 else if( zone->GetNetname() ==
"LV_NET" )
1660 hvZone, lvZone,
F_Cu );
1663 <<
" (expected " <<
pcbIUScale.mmToIU( 4.6 ) <<
")" );
1668 hvZone, lvZone,
In1_Cu );
1671 <<
" (expected " <<
pcbIUScale.mmToIU( 2.3 ) <<
")" );
1678 std::vector<DRC_ITEM> violations;
1683 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
1684 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
1688 BOARD_ITEM* item_a = m_board->ResolveItem( aItem->GetMainItemID() );
1689 BOARD_ITEM* item_b = m_board->ResolveItem( aItem->GetAuxItemID() );
1691 ZONE* zone_a =
dynamic_cast<ZONE*
>( item_a );
1692 ZONE* zone_b =
dynamic_cast<ZONE*
>( item_b );
1694 if( zone_a && zone_b )
1697 << aLayer <<
": " << aItem->GetErrorMessage(
true ) );
1698 violations.push_back( *aItem );
1711 KI_TEST::LoadBoard( m_settingsManager,
"issue23332_min_width/issue23332_min_width", m_board );
1717 for(
ZONE* zone : m_board->Zones() )
1719 int half_min_width = zone->GetMinThickness() / 2;
1726 if( !zone->HasFilledPolysForLayer( layer ) )
1729 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1731 if( !fill || fill->OutlineCount() == 0 )
1736 for(
int ii = 0; ii < fill->OutlineCount(); ii++ )
1741 for(
int jj = 0; jj < fill->HoleCount( ii ); jj++ )
1742 island.
AddHole( fill->Hole( ii, jj ) );
1744 double originalArea = island.
Area();
1746 if( originalArea <= 0 )
1757 double prunedArea =
test.Area();
1758 double areaLoss = ( originalArea - prunedArea ) / originalArea;
1761 "Zone %s layer %d island %d: area=%.0f, loss=%.4f%%",
1762 zone->GetNetname(),
static_cast<int>( layer ), ii,
1763 originalArea, areaLoss * 100.0 ) );
1765 BOOST_CHECK_MESSAGE( areaLoss < 0.01,
1767 "Zone %s layer %d island %d lost %.2f%% area from "
1768 "min-width pruning (min_width=%.3fmm)",
1769 zone->GetNetname(),
static_cast<int>( layer ), ii,
1771 zone->GetMinThickness()
1772 /
static_cast<double>(
pcbIUScale.IU_PER_MM ) ) );
1787 for(
ZONE* zone : m_board->Zones() )
1789 int half_min_width = zone->GetMinThickness() / 2;
1796 if( !zone->HasFilledPolysForLayer( layer ) )
1799 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1801 if( !fill || fill->OutlineCount() == 0 )
1804 for(
int ii = 0; ii < fill->OutlineCount(); ii++ )
1809 for(
int jj = 0; jj < fill->HoleCount( ii ); jj++ )
1810 island.
AddHole( fill->Hole( ii, jj ) );
1812 double originalArea = island.
Area();
1814 if( originalArea <= 0 )
1825 double prunedArea =
test.Area();
1826 double areaLoss = ( originalArea - prunedArea ) / originalArea;
1828 BOOST_CHECK_MESSAGE( areaLoss < 0.01,
1830 "Zone %s (priority %d) layer %d island %d lost "
1831 "%.2f%% area from min-width pruning, suggesting "
1832 "degenerate geometry from overlapping same-net zones",
1834 zone->GetAssignedPriority(),
1835 static_cast<int>( layer ), ii,
1836 areaLoss * 100.0 ) );
1848 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
1851 auto runAreaLossCheck =
1852 [
this](
bool aIterative )
1862 for(
ZONE* zone : m_board->Zones() )
1864 int half_min_width = zone->GetMinThickness() / 2;
1871 if( !zone->HasFilledPolysForLayer( layer ) )
1874 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1876 if( !fill || fill->OutlineCount() == 0 )
1879 for(
int ii = 0; ii < fill->OutlineCount(); ii++ )
1884 for(
int jj = 0; jj < fill->HoleCount( ii ); jj++ )
1885 island.
AddHole( fill->Hole( ii, jj ) );
1887 double originalArea = island.
Area();
1889 if( originalArea <= 0 )
1900 double prunedArea =
test.Area();
1901 double areaLoss = ( originalArea - prunedArea ) / originalArea;
1903 BOOST_CHECK_MESSAGE(
1906 "Zone %s (priority %d) layer %d island %d lost "
1907 "%.2f%% area (iterative=%d), suggesting degenerate "
1908 "geometry from different-net zone knockouts",
1909 zone->GetNetname(), zone->GetAssignedPriority(),
1910 static_cast<int>( layer ), ii, areaLoss * 100.0,
1917 runAreaLossCheck(
false );
1918 runAreaLossCheck(
true );
1943 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
1946 KI_TEST::LoadBoard( m_settingsManager,
"issue23535_minimal/issue23535_minimal", m_board );
1950 ZONE* gndZone =
nullptr;
1952 for(
ZONE* zone : m_board->Zones() )
1954 if( zone->GetNetname() ==
"GND" )
1972 bool hasSpokeToNowhere = gndFill->Contains( spokeTestPoint );
1974 BOOST_CHECK_MESSAGE( !hasSpokeToNowhere,
1975 "GND zone fill contains copper at the thermal gap test point (7.4, 5.0), "
1976 "indicating a thermal relief spoke to nowhere (issue 23535)." );
1983 bool hasValidSpoke = gndFill->Contains( validSpokePoint );
1985 BOOST_CHECK_MESSAGE( hasValidSpoke,
1986 "GND zone fill does not contain copper at the valid spoke test point "
1987 "(5.6, 5.0). The fix may have incorrectly removed valid spokes." );
2005 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2014 if( !commit.
Empty() )
2022 int teardropCount = 0;
2024 for(
ZONE* zone : m_board->Zones() )
2026 if( !zone->IsTeardropArea() )
2044 for(
int i = 0; i <
chain.PointCount(); i++ )
2046 int dy =
chain.CPoint( i ).y - trackY;
2049 maxAbove = std::max( maxAbove, -dy );
2051 maxBelow = std::max( maxBelow, dy );
2055 BOOST_CHECK_MESSAGE( maxAbove > 0 && maxBelow > 0,
2056 "Teardrop should extend on both sides of the track axis" );
2058 if( maxAbove > 0 && maxBelow > 0 )
2062 double ratio =
static_cast<double>( std::min( maxAbove, maxBelow ) )
2063 /
static_cast<double>( std::max( maxAbove, maxBelow ) );
2065 BOOST_CHECK_MESSAGE( ratio > 0.7,
2066 wxString::Format(
"Teardrop asymmetry ratio %.2f is too low "
2067 "(above=%d, below=%d). Expected roughly "
2068 "symmetric about the track axis.",
2069 ratio, maxAbove, maxBelow ) );
2073 BOOST_CHECK_MESSAGE( teardropCount > 0,
"Expected at least one teardrop zone for off-center track" );
2090 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2099 if( !commit.
Empty() )
2105 PAD* testPad =
nullptr;
2107 for(
FOOTPRINT* fp : m_board->Footprints() )
2109 for(
PAD*
pad : fp->Pads() )
2111 if(
pad->GetNumber() ==
"7" )
2119 BOOST_REQUIRE_MESSAGE( testPad !=
nullptr,
"Could not find pad 7 in test board" );
2122 int tolerance = std::max( m_board->GetDesignSettings().m_MaxError,
2128 int teardropCount = 0;
2130 for(
ZONE* zone : m_board->Zones() )
2132 if( !zone->IsTeardropArea() )
2137 BOOST_REQUIRE_MESSAGE( outline && outline->
OutlineCount() > 0,
2138 "Teardrop zone has no outline" );
2152 for(
int i = 0; i <
chain.PointCount(); i++ )
2155 double distToPad = (
VECTOR2D( pt ) -
VECTOR2D( padCenter ) ).EuclideanNorm();
2156 double distToTrack = (
VECTOR2D( pt ) -
VECTOR2D( trackAnchor ) ).EuclideanNorm();
2159 if( distToPad < distToTrack )
2161 BOOST_CHECK_MESSAGE(
2163 wxString::Format(
"Teardrop vertex (%d, %d) is outside the pad "
2164 "outline with %d nm tolerance",
2165 pt.
x, pt.
y, tolerance ) );
2170 BOOST_CHECK_MESSAGE( teardropCount > 0,
2171 "Expected at least one teardrop zone for elongated pad" );
2185 auto runVariant = [&](
bool aCurvedEdges )
2189 for(
PCB_TRACK* track : m_board->Tracks() )
2193 static_cast<PCB_VIA*
>( track )->SetTeardropCurved( aCurvedEdges );
2199 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2208 if( !commit.
Empty() )
2211 int teardropCount = 0;
2212 bool foundSelfIntersection =
false;
2214 for(
ZONE* zone : m_board->Zones() )
2216 if( !zone->IsTeardropArea() )
2227 int n =
chain.PointCount();
2229 for(
int i = 0; i < n && !foundSelfIntersection; i++ )
2231 SEG segA(
chain.CPoint( i ),
chain.CPoint( ( i + 1 ) % n ) );
2233 for(
int j = i + 2; j < n; j++ )
2235 if( i == 0 && j == n - 1 )
2238 SEG segB(
chain.CPoint( j ),
chain.CPoint( ( j + 1 ) % n ) );
2241 if( hit.has_value() )
2244 "Self-intersection at (%d, %d) between edges %d and %d "
2246 hit->x, hit->y, i, j,
2247 aCurvedEdges ?
"yes" :
"no" ) );
2249 for(
int k = 0; k < n; k++ )
2252 " pt[%d] = (%d, %d)", k,
2253 chain.CPoint( k ).x,
chain.CPoint( k ).y ) );
2256 foundSelfIntersection =
true;
2263 BOOST_CHECK_MESSAGE( teardropCount > 0,
2264 wxString::Format(
"Expected at least one teardrop zone "
2266 aCurvedEdges ?
"yes" :
"no" ) );
2268 BOOST_CHECK_MESSAGE( !foundSelfIntersection,
2269 wxString::Format(
"Teardrop polygon has self-intersecting "
2270 "edges (curved=%s)",
2271 aCurvedEdges ?
"yes" :
"no" ) );
2275 runVariant(
false );
2299 auto runVariant = [&](
bool aCurvedEdges )
2306 for(
PCB_TRACK* track : m_board->Tracks() )
2311 via->SetTeardropCurved( aCurvedEdges );
2312 viaPos =
via->GetPosition();
2321 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2330 if( !commit.
Empty() )
2335 const double maxBackSideDist = viaRadius * 1.2;
2336 int teardropCount = 0;
2337 int spikingPoints = 0;
2339 double worstDistance = 0.0;
2341 for(
ZONE* zone : m_board->Zones() )
2343 if( !zone->IsTeardropArea() )
2355 for(
int i = 0; i <
chain.PointCount(); i++ )
2366 if( dist > maxBackSideDist )
2370 if( dist > worstDistance )
2372 worstDistance = dist;
2379 BOOST_CHECK_MESSAGE( teardropCount == 0,
2380 wxString::Format(
"Expected no teardrop on grazing-entry "
2381 "track (emergence below track width), got "
2384 aCurvedEdges ?
"yes" :
"no" ) );
2386 BOOST_CHECK_MESSAGE( spikingPoints == 0,
2387 wxString::Format(
"Found %d teardrop polygon vertex/vertices "
2388 "outside the expected envelope (worst at "
2389 "(%d, %d), %f mm from via center; curved=%s)",
2391 worstPoint.
x, worstPoint.
y,
2393 aCurvedEdges ?
"yes" :
"no" ) );
2397 runVariant(
false );
2415 auto runVariant = [&](
bool aCurvedEdges )
2419 for(
PCB_TRACK* track : m_board->Tracks() )
2423 static_cast<PCB_VIA*
>( track )->SetTeardropCurved( aCurvedEdges );
2429 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2438 if( !commit.
Empty() )
2441 int teardropCount = 0;
2442 int selfIntersectingCount = 0;
2445 for(
ZONE* zone : m_board->Zones() )
2447 if( !zone->IsTeardropArea() )
2458 int n =
chain.PointCount();
2459 bool intersected =
false;
2461 for(
int i = 0; i < n && !intersected; i++ )
2463 SEG segA(
chain.CPoint( i ),
chain.CPoint( ( i + 1 ) % n ) );
2465 for(
int j = i + 2; j < n; j++ )
2467 if( i == 0 && j == n - 1 )
2470 SEG segB(
chain.CPoint( j ),
chain.CPoint( ( j + 1 ) % n ) );
2473 if( hit.has_value() )
2476 "Teardrop polygon self-intersection at (%d, %d) "
2477 "between edges %d and %d (curved=%s)",
2478 hit->x, hit->y, i, j, aCurvedEdges ?
"yes" :
"no" ) );
2480 worstPoint = hit.value();
2488 selfIntersectingCount++;
2491 BOOST_CHECK_MESSAGE( teardropCount > 0,
2492 wxString::Format(
"Expected at least one teardrop zone "
2494 aCurvedEdges ?
"yes" :
"no" ) );
2496 BOOST_CHECK_MESSAGE( selfIntersectingCount == 0,
2497 wxString::Format(
"%d of %d teardrop polygon(s) self-intersect "
2498 "(worst at (%d, %d); curved=%s)",
2499 selfIntersectingCount, teardropCount,
2500 worstPoint.
x, worstPoint.
y,
2501 aCurvedEdges ?
"yes" :
"no" ) );
2505 runVariant(
false );
2513 auto runVariant = [&](
const wxString& aFixture,
bool aCurvedEdges )
2517 for(
PCB_TRACK* track : m_board->Tracks() )
2520 static_cast<PCB_VIA*
>( track )->SetTeardropCurved( aCurvedEdges );
2523 for(
FOOTPRINT* footprint : m_board->Footprints() )
2525 for(
PAD*
pad : footprint->Pads() )
2526 pad->SetTeardropCurved( aCurvedEdges );
2530 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2539 if( !commit.
Empty() )
2542 int teardropCount = 0;
2543 VECTOR2I padPos = ( *m_board->Footprints().begin() )->Pads()[0]->GetPosition();
2544 bool padHasTeardrop =
false;
2546 for(
ZONE* zone : m_board->Zones() )
2548 if( !zone->IsTeardropArea() )
2553 if( zone->Outline()->Contains( padPos ) )
2554 padHasTeardrop =
true;
2557 BOOST_CHECK_MESSAGE( padHasTeardrop,
2558 wxString::Format(
"Expected the pad-anchored teardrop on the short track joining the "
2559 "pad and the close via in %s, got %d teardrop(s) (curved=%s)",
2560 aFixture, teardropCount, aCurvedEdges ?
"yes" :
"no" ) );
2563 runVariant(
"teardrop_close_via",
true );
2564 runVariant(
"teardrop_close_via",
false );
2565 runVariant(
"teardrop_close_via_rotated_pad",
true );
2566 runVariant(
"teardrop_close_via_rotated_pad",
false );
2582 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
2585 auto getTotalFilledArea =
2590 for(
ZONE* zone : m_board->Zones() )
2592 if( zone->GetIsRuleArea() )
2597 if( !zone->HasFilledPolysForLayer( layer ) )
2600 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
2610 auto refillAndMeasure =
2611 [
this, &cfg, &getTotalFilledArea](
bool aIterative ) ->
double
2617 double storedArea = getTotalFilledArea();
2619 BOOST_REQUIRE_MESSAGE( storedArea > 0,
"Stored v9 fill has zero area" );
2622 return getTotalFilledArea();
2627 double storedArea = getTotalFilledArea();
2629 BOOST_REQUIRE_MESSAGE( storedArea > 0,
"Stored v9 fill has zero area" );
2631 double nonIterativeArea = refillAndMeasure(
false );
2632 double iterativeArea = refillAndMeasure(
true );
2633 double nonIterativeAreaRatio = nonIterativeArea / storedArea;
2634 double iterativeAreaRatio = iterativeArea / storedArea;
2636 BOOST_CHECK_MESSAGE(
2637 nonIterativeAreaRatio > 0.99999,
2639 "Non-iterative refill lost %.4f%% versus stored v9 fill "
2640 "(stored=%.2f mm^2, non-iterative=%.2f mm^2). "
2641 "This suggests missing pieces near keepout boundaries (issue 23515).",
2642 ( 1.0 - nonIterativeAreaRatio ) * 100.0,
2643 storedArea / 1e6, nonIterativeArea / 1e6 ) );
2645 BOOST_CHECK_MESSAGE(
2646 iterativeAreaRatio > 0.99999,
2648 "Iterative refill lost %.4f%% versus stored v9 fill "
2649 "(stored=%.2f mm^2, iterative=%.2f mm^2). "
2650 "This suggests missing pieces near keepout boundaries (issue 23515).",
2651 ( 1.0 - iterativeAreaRatio ) * 100.0,
2652 storedArea / 1e6, iterativeArea / 1e6 ) );
2666 m_board = std::make_unique<BOARD>();
2669 m_board->SetCopperLayerCount( 2 );
2678 m_board->Add( gndNet );
2690 int hatchThickness =
pcbIUScale.mmToIU( 0.3 );
2699 via->SetPosition( viaPos );
2701 via->SetDrill( viaDrill );
2703 via->SetNetCode( gndNetCode );
2704 m_board->Add(
via );
2708 auto makeHatchZone =
2711 ZONE* zone =
new ZONE( m_board.get() );
2730 m_board->Add( zone );
2737 m_board->BuildConnectivity();
2738 auto drcEngine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
2739 drcEngine->InitEngine( wxFileName() );
2751 double fullFillArea = 0.0;
2752 double thermalFillArea = 0.0;
2763 "Zone should have fill on F.Cu with FULL connection" );
2767 for(
int i = 0; i < fill->OutlineCount(); i++ )
2768 fullFillArea +=
std::abs( fill->Outline( i ).Area() );
2770 m_board->Remove(
via );
2771 m_board->Remove( zone );
2785 "Zone should have fill on F.Cu with THERMAL connection" );
2789 for(
int i = 0; i < fill->OutlineCount(); i++ )
2790 thermalFillArea +=
std::abs( fill->Outline( i ).Area() );
2792 m_board->Remove(
via );
2793 m_board->Remove( zone );
2802 double areaThreshold = 0.2 * iuPerMM * iuPerMM;
2804 double areaIU2toMM2 = 1.0 / ( iuPerMM * iuPerMM );
2806 BOOST_CHECK_MESSAGE( thermalFillArea > fullFillArea + areaThreshold,
2808 "THERMAL connection fill area (%.2f sq mm) should be larger "
2809 "than FULL fill area (%.2f sq mm) by at least 0.2 sq mm. "
2810 "If they are equal or FULL is larger, thermal ring was not "
2811 "added for THERMAL connection, or thermal ring was incorrectly "
2812 "added for FULL connection (issue 23516 regression).",
2813 thermalFillArea * areaIU2toMM2, fullFillArea * areaIU2toMM2 ) );
2825 m_board = std::make_unique<BOARD>();
2826 m_board->SetCopperLayerCount( 2 );
2833 m_board->Add( gndNet );
2842 int hatchThickness =
pcbIUScale.mmToIU( 0.3 );
2844 auto makeHatchZone = [&]() ->
ZONE*
2846 ZONE* zone =
new ZONE( m_board.get() );
2863 m_board->Add( zone );
2869 const int steps = 8;
2870 const double startMM = 9.0;
2871 const double stepMM = 2.3 / steps;
2873 int isolatedCount = 0;
2874 int testedCount = 0;
2876 for(
int ix = 0; ix < steps; ix++ )
2878 for(
int iy = 0; iy < steps; iy++ )
2883 via->SetPosition( viaPos );
2885 via->SetDrill( viaDrill );
2887 via->SetNetCode( gndNetCode );
2888 m_board->Add(
via );
2890 ZONE* zone = makeHatchZone();
2892 m_board->BuildConnectivity();
2893 auto drcEngine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
2894 drcEngine->InitEngine( wxFileName() );
2902 std::shared_ptr<SHAPE> viaShape =
via->GetEffectiveShape(
F_Cu );
2906 if( !fill->Collide( viaShape.get(), 0 ) )
2911 m_board->Remove(
via );
2912 m_board->Remove( zone );
2918 BOOST_CHECK_MESSAGE( isolatedCount == 0, wxString::Format(
"%d of %d FULL-connection via positions were left "
2919 "isolated from the hatch fill (issue 24559).",
2920 isolatedCount, testedCount ) );
2951 ~ScopeGuard() { ref = orig; }
2957 const std::vector<std::string> checkedNames = {
"hi1",
"hi2",
"hi3",
"hi4",
"hi5",
"hi6",
"hi7",
2958 "lo1",
"lo2",
"lo3",
"lo4",
"lo5",
"lo6" };
2959 std::map<std::string, ZONE*> zoneByName;
2961 for(
ZONE* zone : m_board->Zones() )
2962 zoneByName[zone->GetZoneName().ToStdString()] = zone;
2964 for(
const std::string&
name : checkedNames )
2966 BOOST_REQUIRE_MESSAGE( zoneByName.count(
name ),
"Zone '" +
name +
"' not found in test board" );
2967 BOOST_REQUIRE_MESSAGE( zoneByName[
name]->HasFilledPolysForLayer(
F_Cu ),
2968 "Zone '" +
name +
"' has no fill on F.Cu" );
2972 for(
const std::string&
name : {
"hi3",
"hi5",
"hi7" } )
2974 int islands = zoneByName[
name]->GetFilledPolysList(
F_Cu )->OutlineCount();
2976 BOOST_CHECK_MESSAGE( islands == 2, wxString::Format(
"Zone '%s' should have 2 filled islands but has %d. "
2977 "Cascading island removal did not converge correctly.",
2982 for(
const std::string&
name : {
"lo1",
"lo2",
"lo3",
"lo4",
"lo5",
"lo6",
"hi2",
"hi4",
"hi6" } )
2984 int islands = zoneByName[
name]->GetFilledPolysList(
F_Cu )->OutlineCount();
2986 BOOST_CHECK_MESSAGE( islands == 1, wxString::Format(
"Zone '%s' should have 1 filled island but has %d. "
2987 "Iterative refill may have incorrectly blocked or "
2988 "expanded this zone.",
3005 ZONE* thievingZone =
nullptr;
3007 for(
ZONE* z : m_board->Zones() )
3024 BOOST_CHECK_GE( fill->OutlineCount(), 2 );
3027 BOX2I fillBox = fill->BBox();
3034 BOOST_CHECK_GT( fill->TotalVertices(), 200 );
3060 ~ScopeGuard() { ref = orig; }
3064 class WarningCapture :
public wxLog
3067 bool m_hadWarning =
false;
3070 void DoLogRecord( wxLogLevel aLevel,
const wxString&,
const wxLogRecordInfo& )
override
3072 if( aLevel == wxLOG_Warning )
3073 m_hadWarning =
true;
3077 auto* capture =
new WarningCapture();
3078 wxLog* oldLog = wxLog::SetActiveTarget( capture );
3083 ~LogGuard() { wxLog::SetActiveTarget( old ); }
3084 } logGuard{ oldLog };
3089 BOOST_CHECK_MESSAGE( capture->m_hadWarning,
"Expected a wxLogWarning when iterative refill hits the iteration "
3090 "limit, but none was emitted. The convergence-limit board may no "
3091 "longer trigger the cap, or the warning path has changed." );
3102 m_board = std::make_unique<BOARD>();
3104 ZONE* zone =
new ZONE( m_board.get() );
3118 m_board->Add( zone );
3127 BOOST_CHECK_GT( fill->OutlineCount(), 5 );
3140 m_board = std::make_unique<BOARD>();
3141 m_board->SetCopperLayerCount( 2 );
3144 ZONE* zone =
new ZONE( m_board.get() );
3164 m_board->Add( zone );
3170 BOOST_REQUIRE_GT( fill->OutlineCount(), 0 );
3174 BOOST_CHECK_GE( fill->OutlineCount(), 6 );
3175 BOOST_CHECK_LE( fill->OutlineCount(), 12 );
3178 const double fullDotArea =
M_PI * std::pow(
pcbIUScale.mmToIU( 0.25 ), 2 );
3191 auto countDots = [](
bool stagger ) ->
int
3193 auto board = std::make_unique<BOARD>();
3194 board->SetCopperLayerCount( 2 );
3196 ZONE* zone =
new ZONE( board.get() );
3217 int plain = countDots(
false );
3218 int staggered = countDots(
true );
3224 BOOST_CHECK_NE( plain, staggered );
3225 BOOST_CHECK_GE( staggered, plain / 2 );
3226 BOOST_CHECK_LE( staggered, plain * 2 );
3237 m_board = std::make_unique<BOARD>();
3238 m_board->SetCopperLayerCount( 2 );
3240 ZONE* zone =
new ZONE( m_board.get() );
3254 m_board->Add( zone );
3260 BOOST_REQUIRE_GT( fill->OutlineCount(), 0 );
3264 BOOST_CHECK_GE( fill->OutlineCount(), 6 );
3265 BOOST_CHECK_LE( fill->OutlineCount(), 12 );
3267 const double fullSquareArea = std::pow(
pcbIUScale.mmToIU( 0.6 ), 2 );
3282 m_board = std::make_unique<BOARD>();
3283 m_board->SetCopperLayerCount( 2 );
3285 ZONE* zone =
new ZONE( m_board.get() );
3299 m_board->Add( zone );
3301 auto start = std::chrono::steady_clock::now();
3303 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
3304 std::chrono::steady_clock::now() - start )
3314 BOOST_CHECK_LT( elapsed, 30000 );
3326 m_board = std::make_unique<BOARD>();
3327 m_board->SetCopperLayerCount( 2 );
3329 ZONE* zone =
new ZONE( m_board.get() );
3343 m_board->Add( zone );
3349 BOOST_REQUIRE_GT( fill->TotalVertices(), 0 );
3359 BOX2I fillBox = fill->BBox();
3367 BOOST_CHECK_GT( fill->TotalVertices(), 30 );
3387 for(
ZONE* zone : m_board->Zones() )
3389 if( zone->GetIsRuleArea() )
3394 if( !zone->HasFilledPolysForLayer( layer ) )
3397 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
3400 total += fill->OutlineCount();
3407 int storedIslands = countIslands();
3409 BOOST_REQUIRE_MESSAGE( storedIslands >= 3,
3410 wxString::Format(
"Stored v9 fill should have at least 3 silk islands; "
3416 int refilledIslands = countIslands();
3418 BOOST_CHECK_MESSAGE( refilledIslands == storedIslands,
3419 wxString::Format(
"Refill lost silk islands: stored=%d, refilled=%d. "
3420 "Outline 0 of every non-copper multi-island zone "
3421 "was being incorrectly removed (issue 24089).",
3422 storedIslands, refilledIslands ) );
3433 m_board = std::make_unique<BOARD>();
3434 m_board->SetCopperLayerCount( 4 );
3441 m_board->Add( gndNet );
3445 m_board->Add( vccNet );
3448 ZONE* gndZone =
new ZONE( m_board.get() );
3465 m_board->Add( gndZone );
3467 ZONE* vccZone =
new ZONE( m_board.get() );
3491 m_board->Add( vccZone );
3495 auto footprint = std::make_unique<FOOTPRINT>( m_board.get() );
3505 pad->SetNetCode( gndNetCode );
3507 footprint->Add(
pad );
3508 footprint->SetPosition(
VECTOR2I( 0, 0 ) );
3509 m_board->Add( footprint.release() );
3511 m_board->BuildConnectivity();
3512 auto drcEngine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
3513 drcEngine->InitEngine( wxFileName() );
3521 "VCC outline must contain the pad position to reproduce issue 24175." );
3523 "VCC zone should still have fill in its right lobe." );
3528 BOOST_REQUIRE_MESSAGE( !vccFill->Contains(
pad->GetPosition() ),
3529 "VCC fill should NOT contain the pad position (corridor must be "
3530 "pruned by min-thickness for the test to exercise issue 24175)." );
3535 BOOST_CHECK_MESSAGE(
pad->FlashLayer(
In1_Cu ),
3536 "PTH pad inside higher-priority different-net zone must still flash "
3537 "when a same-net lower-priority zone covers it (issue 24175)." );
3540 "GND zone should have fill on In1.Cu" );
3547 bool foundCopperAround =
false;
3549 for(
int i = 0; i < samples; i++ )
3551 double angle = ( 2.0 *
M_PI * i ) / samples;
3553 pad->GetPosition().y +
KiROUND( sampleR * std::sin( angle ) ) );
3555 if( gndFill->Contains( p ) )
3557 foundCopperAround =
true;
3562 BOOST_CHECK_MESSAGE( foundCopperAround,
3563 "Lower-priority GND zone should have copper around GND pad even when "
3564 "a higher-priority different-net zone outline contains the pad "
3587 PCB_LAYER_ID targetLayer = m_board->GetLayerID( wxT(
"B.Cu" ) );
3588 std::vector<ZONE*> toFill;
3590 for(
ZONE* zone : m_board->Zones() )
3592 if( zone->GetIsRuleArea() )
3595 if( !zone->IsOnLayer( targetLayer ) )
3598 if( zone->IsFilled() )
3601 toFill.push_back( zone );
3604 BOOST_REQUIRE_MESSAGE( !toFill.empty(),
3605 "Expected at least one unfilled B.Cu zone to exercise the API path." );
3612 BOOST_CHECK_NO_THROW( filler.
Fill( toFill ) );
3626 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
3634 const int margin =
pcbIUScale.mmToIU( 0.05 );
3636 std::map<int, SHAPE_POLY_SET> mergedByNet;
3638 for(
ZONE* zone : m_board->Zones() )
3640 if( zone->GetIsRuleArea() || !zone->HasFilledPolysForLayer( layer ) )
3643 mergedByNet[zone->GetNetCode()].BooleanAdd( *zone->GetFilledPolysList( layer ) );
3648 auto buildLegitVoids =
3654 for(
ZONE* other : m_board->Zones() )
3656 if( !other->GetLayerSet().Contains( layer ) )
3659 if( other->GetIsRuleArea() )
3661 if( other->GetDoNotAllowZoneFills() )
3667 if( other->GetNetCode() == aLower->
GetNetCode()
3669 || other->GetAssignedPriority() <= aHigher->GetAssignedPriority()
3670 || !other->HasFilledPolysForLayer( layer ) )
3683 std::vector<ZONE*> zones;
3685 for(
ZONE* zone : m_board->Zones() )
3687 if( !zone->GetIsRuleArea() && zone->GetNetCode() > 0 && zone->GetLayerSet().Contains( layer ) )
3688 zones.push_back( zone );
3691 int checkedPairs = 0;
3693 for(
size_t i = 0; i < zones.size(); ++i )
3695 for(
size_t j = i + 1; j < zones.size(); ++j )
3710 const ZONE* higher = ( lower == a ) ? b : a;
3723 double uncoveredArea =
3726 BOOST_CHECK_MESSAGE( uncoveredArea < 0.01,
3727 wxString::Format(
"Same-net zones (priorities %d and %d) left %.4f mm^2 of "
3728 "their overlap unfilled; overlapping same-net zones must "
3729 "merge (issue 23790).",
3736 BOOST_CHECK_MESSAGE( checkedPairs >= 2,
3737 wxString::Format(
"Expected at least two overlapping same-net zone pairs "
3738 "to exercise the merge, found %d.", checkedPairs ) );
3751 ~ScopeGuard() { ref = orig; }
3754 for(
bool iterative : {
true,
false } )
3762 ZONE* hatched =
nullptr;
3763 ZONE* solid =
nullptr;
3765 for(
ZONE* zone : m_board->Zones() )
3794 double leakedArea = leaked.
Area() / mm2;
3796 BOOST_CHECK_MESSAGE( leakedArea < 0.01,
3797 wxString::Format(
"%s: lower-priority zone poured %.3f mm^2 inside the "
3798 "higher-priority hatched zone (issue 24935).",
3799 iterative ? wxS(
"iterative refill" ) : wxS(
"single pass" ),
3814 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
3822 for(
int pass = 0; pass < 64; ++pass )
3830 for(
ZONE* zone : m_board->Zones() )
3832 if( zone->GetIsRuleArea() || !zone->HasFilledPolysForLayer( layer ) )
3835 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
3841 BOOST_CHECK_MESSAGE( merged.
Area() > 0.0,
3842 wxString::Format(
"Fill pass %d produced no copper.", pass ) );
3856 auto outlineNoArcs =
3864 int checkedPairs = 0;
3866 for(
ZONE* hatch : m_board->Zones() )
3869 || !hatch->HasFilledPolysForLayer( layer ) )
3877 for(
ZONE* other : m_board->Zones() )
3879 if( other == hatch || other->GetIsRuleArea() || !other->GetLayerSet().Contains( layer )
3880 || other->GetAssignedPriority() <= hatch->GetAssignedPriority() )
3903 if( band.
Area() <= 0 )
3909 double coverage = covered.
Area() / band.
Area();
3912 BOOST_CHECK_MESSAGE( coverage >= 0.85,
3913 wxString::Format(
"Hatch zone %s carved by %s: border ring only %.0f%% filled; "
3914 "the hatch border was not re-established around the carved "
3915 "area (issue 24758).",
3916 hatch->GetZoneName(), other->GetZoneName(),
3917 coverage * 100.0 ) );
3921 BOOST_CHECK_MESSAGE( checkedPairs >= 3,
3922 wxString::Format(
"Expected at least three carved hatch borders to check, "
3923 "found %d.", checkedPairs ) );
3931 m_board = std::make_unique<BOARD>();
3942 m_board->Add( netA );
3943 m_board->Add( netB );
3949 const int sep =
clearance + extraMargin + maxError / 2;
3950 const int ax =
pcbIUScale.mmToIU( 100 ) + sep;
3952 ZONE* zoneA =
new ZONE( m_board.get() );
3960 m_board->Add( zoneA );
3964 ZONE* zoneB =
new ZONE( m_board.get() );
3973 for(
int ii = 0; ii < 1000; ++ii )
3981 m_board->Add( zoneB );
3993 BOOST_CHECK_MESSAGE( !fillA->Collide( fillB.get(),
clearance + extraMargin + maxError * 3 / 4 ),
3994 "Lower-priority zone filled before the higher-priority knockout was "
3995 "published; the fill depends on thread scheduling." );
4020 std::vector<DRC_ITEM> violations;
4023 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
4024 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
4027 violations.push_back( *aItem );
4032 if( !violations.empty() )
4036 std::map<KIID, EDA_ITEM*> itemMap;
4037 m_board->FillItemMap( itemMap );
4039 for(
const DRC_ITEM& item : violations )
4043 BOOST_CHECK_MESSAGE( violations.empty(),
4044 wxString::Format(
"Zone fill produced %zu connection_width violations; "
4045 "expected 0 (issue 24312).",
4046 violations.size() ) );
4059 ~ScopeGuard() { ref = orig; }
4077 double localCopperArea = 0.0;
4079 for(
ZONE* zone : m_board->Zones() )
4081 if( !zone->IsOnLayer( layer ) )
4084 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
4097 localCopperArea = std::max( localCopperArea,
std::abs( local.
Area() ) );
4102 BOOST_CHECK_MESSAGE( localCopperArea > minimumLocalCopperArea,
4103 wxString::Format(
"Expected copper around the bridge on %s; the "
4104 "bridge must be widened, not removed.",
4108 std::vector<DRC_ITEM> violations;
4111 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I&,
int,
4115 violations.push_back( *aItem );
4120 BOOST_CHECK_MESSAGE( violations.empty(),
4121 wxString::Format(
"Iterative refill produced %zu connection_width "
4122 "violations; expected full-width bridges (issue 24835).",
4123 violations.size() ) );
constexpr int ARC_HIGH_DEF
constexpr EDA_IU_SCALE pcbIUScale
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
std::map< int, SEVERITY > m_DRCSeverities
std::shared_ptr< DRC_ENGINE > m_DRCEngine
void SetCopperLayerCount(int aNewLayerCount)
Set the copper layer count to aNewLayerCount.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
constexpr coord_type GetLeft() const
constexpr coord_type GetRight() const
constexpr coord_type GetTop() const
constexpr coord_type GetBottom() const
const MINOPTMAX< int > & GetValue() const
void RunTests(EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aTestFootprints, BOARD_COMMIT *aCommit=nullptr)
Run the DRC tests.
void SetViolationHandler(DRC_VIOLATION_HANDLER aHandler)
Set an optional DRC violation handler (receives DRC_ITEMs and positions).
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
void SetClearance(int aClearance)
Handle the data for a net.
std::shared_ptr< NETCLASS > GetDefaultNetclass() const
Gets the default netclass for the project.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
const wxString & GetNumber() const
VECTOR2I GetPosition() const override
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
ecoord SquaredDistance(const SEG &aSeg) const
VECTOR2I::extended_type ecoord
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
double Area(bool aAbsolute=true) const
Return the area of this chain.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
double Area()
Return the area of this poly set.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
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.
SHAPE_POLY_SET CloneDropTriangulation() const
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.
TEARDROP_MANAGER manage and build teardrop areas A teardrop area is a polygonal area (a copper ZONE) ...
void UpdateTeardrops(BOARD_COMMIT &aCommit, const std::vector< BOARD_ITEM * > *dirtyPadsAndVias, const std::set< PCB_TRACK * > *dirtyTracks, bool aForceFullUpdate=false)
Update teardrops on a list of items.
TEARDROP_PARAMETARS is a helper class to handle parameters needed to build teardrops for a board thes...
int m_TdMaxLen
max allowed length for teardrops in IU. <= 0 to disable
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
bool Fill(const std::vector< ZONE * > &aZones, bool aCheck=false, wxWindow *aParent=nullptr)
Fills the given list of zones.
Handle a list of polygons defining a copper zone.
void SetHatchThickness(int aThickness)
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
void SetMinThickness(int aMinThickness)
double GetFilledArea()
This area is cached from the most recent call to CalculateFilledArea().
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
SHAPE_POLY_SET * Outline()
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
void SetFillMode(ZONE_FILL_MODE aFillMode)
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
void SetThievingSettings(const THIEVING_SETTINGS &aSettings)
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
void SetThermalReliefGap(int aThermalReliefGap)
bool AppendCorner(VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
double CalculateFilledArea()
Compute the area currently occupied by the zone fill.
void SetAssignedPriority(unsigned aPriority)
void SetPadConnection(ZONE_CONNECTION aPadConnection)
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
void SetHatchGap(int aStep)
unsigned GetAssignedPriority() const
bool SameNet(const ZONE *aOther) const
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ ROUND_ALL_CORNERS
All angles are rounded.
static constexpr EDA_ANGLE ANGLE_0
bool m_ZoneFillIterativeRefill
Enable iterative zone filling to handle isolated islands in higher priority zones.
PCB_LAYER_ID
A quick note on layer IDs:
void LoadBoard(SETTINGS_MANAGER &aSettingsManager, const wxString &aRelPath, std::unique_ptr< BOARD > &aBoard)
void FillZones(BOARD *m_board)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ PTH
Plated through hole pad.
@ REMOVE_EXCEPT_START_AND_END
std::optional< VECTOR2I > OPT_VECTOR2I
Parameters that drive copper-thieving fill generation.
std::unique_ptr< BOARD > m_board
SETTINGS_MANAGER m_settingsManager
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
const SHAPE_LINE_CHAIN chain
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
BOOST_CHECK_EQUAL(result, "25.4")
static const std::vector< wxString > RegressionZoneFillTests_tests
static const std::vector< std::pair< wxString, int > > RegressionTeardropFill_tests
BOOST_DATA_TEST_CASE_F(ZONE_FILL_TEST_FIXTURE, RegressionZoneFillTests, boost::unit_test::data::make(RegressionZoneFillTests_tests), relPath)
static void CheckAllOutlineAreasAtLeast(const std::shared_ptr< SHAPE_POLY_SET > &aFill, double aMinArea, const wxString &aLabel)
Assert every outline in aFill has at least aMinArea — used to verify thieving stamps survived the fil...
static const std::vector< wxString > RegressionSliverZoneFillTests_tests
BOOST_FIXTURE_TEST_CASE(BasicZoneFills, ZONE_FILL_TEST_FIXTURE)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D
ZONE_CONNECTION
How pads are covered by copper in zone.
@ THERMAL
Use thermal relief for pads.
@ FULL
pads are covered by copper