25#include <boost/test/data/test_case.hpp>
50 double aMinArea,
const wxString& aLabel )
52 for(
int ii = 0; ii < aFill->OutlineCount(); ++ii )
54 const double area =
std::abs( aFill->Outline( ii ).Area() );
57 wxString::Format(
"%s %d area %.0f IU^2 below %.0f IU^2; partial "
58 "stamps should not survive.",
59 aLabel, ii, area, aMinArea ) );
89 for(
PAD*
pad : m_board->Footprints()[0]->Pads() )
91 if(
pad->GetNumber() ==
"2" ||
pad->GetNumber() ==
"4" ||
pad->GetNumber() ==
"6" )
102 for(
PCB_TRACK* track : m_board->Tracks() )
110 arc8 = track->m_Uuid;
111 track->SetWidth( track->GetWidth() +
delta +
delta );
115 arc12 = track->m_Uuid;
121 bool foundPad2Error =
false;
122 bool foundPad4Error =
false;
123 bool foundPad6Error =
false;
124 bool foundArc8Error =
false;
125 bool foundArc12Error =
false;
126 bool foundOtherError =
false;
131 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
132 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
136 BOARD_ITEM* item_a = m_board->ResolveItem( aItem->GetMainItemID() );
137 PAD* pad_a =
dynamic_cast<PAD*
>( item_a );
140 BOARD_ITEM* item_b = m_board->ResolveItem( aItem->GetAuxItemID() );
141 PAD* pad_b =
dynamic_cast<PAD*
>( item_b );
144 if( pad_a && pad_a->
GetNumber() ==
"2" ) foundPad2Error =
true;
145 else if( pad_a && pad_a->
GetNumber() ==
"4" ) foundPad4Error =
true;
146 else if( pad_a && pad_a->
GetNumber() ==
"6" ) foundPad6Error =
true;
147 else if( pad_b && pad_b->
GetNumber() ==
"2" ) foundPad2Error =
true;
148 else if( pad_b && pad_b->
GetNumber() ==
"4" ) foundPad4Error =
true;
149 else if( pad_b && pad_b->
GetNumber() ==
"6" ) foundPad6Error =
true;
150 else if( trk_a && trk_a->
m_Uuid == arc8 ) foundArc8Error =
true;
151 else if( trk_a && trk_a->
m_Uuid == arc12 ) foundArc12Error =
true;
152 else if( trk_b && trk_b->
m_Uuid == arc8 ) foundArc8Error =
true;
153 else if( trk_b && trk_b->
m_Uuid == arc12 ) foundArc12Error =
true;
154 else foundOtherError =
true;
184 for(
ZONE* zone : m_board->Zones() )
186 if( zone->GetLayerSet().Contains(
F_Cu ) )
223 std::vector<DRC_ITEM> violations;
226 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
227 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
230 violations.push_back( *aItem );
235 if( violations.empty() )
244 std::map<KIID, EDA_ITEM*> itemMap;
245 m_board->FillItemMap( itemMap );
247 for(
const DRC_ITEM& item : violations )
250 BOOST_ERROR( wxString::Format(
"Zone fill regression: %s failed", relPath ) );
269 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
272 auto runDrcClearanceCheck =
273 [
this](
bool aIterative ) ->
int
284 std::vector<DRC_ITEM> violations;
286 std::map<KIID, EDA_ITEM*> itemMap;
287 m_board->FillItemMap( itemMap );
291 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
293 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
297 BOARD_ITEM* itemA = m_board->ResolveItem( aItem->GetMainItemID() );
298 BOARD_ITEM* itemB = m_board->ResolveItem( aItem->GetAuxItemID() );
300 if(
dynamic_cast<ZONE*
>( itemA ) &&
dynamic_cast<ZONE*
>( itemB ) )
302 violations.push_back( *aItem );
305 aItem->ShowReport( &unitsProvider,
313 return static_cast<int>( violations.size() );
316 int iterativeViolations = runDrcClearanceCheck(
true );
319 wxString::Format(
"Iterative refill produced %d zone-to-zone clearance "
320 "violations (expected 0)", iterativeViolations ) );
322 int nonIterativeViolations = runDrcClearanceCheck(
false );
325 wxString::Format(
"Non-iterative refill produced %d zone-to-zone clearance "
326 "violations (expected 0)", nonIterativeViolations ) );
345 std::vector<DRC_ITEM> violations;
348 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
349 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
352 violations.push_back( *aItem );
357 if( violations.empty() )
360 BOOST_TEST_MESSAGE( wxString::Format(
"Zone fill copper sliver regression: %s passed", relPath ) );
366 std::map<KIID, EDA_ITEM*> itemMap;
367 m_board->FillItemMap( itemMap );
369 for(
const DRC_ITEM& item : violations )
372 BOOST_ERROR( wxString::Format(
"Zone fill copper sliver regression: %s failed", relPath ) );
378 {
"teardrop_issue_JPC2", 5 },
385 const wxString& relPath =
test.first;
386 const int count =
test.second;
396 for(
ZONE* zone : m_board->Zones() )
398 if( zone->IsTeardropArea() )
403 << relPath <<
", found "
411 std::vector<wxString> tests = { {
"issue19956/issue19956" }
414 for(
const wxString& relPath : tests )
420 for(
ZONE* zone : m_board->Zones() )
424 std::shared_ptr<SHAPE> a_shape( zone->GetEffectiveShape( layer ) );
426 for(
PAD*
pad : m_board->GetPads() )
428 std::shared_ptr<SHAPE> pad_shape(
pad->GetEffectiveShape( layer ) );
429 int clearance = pad_shape->GetClearance( a_shape.get() );
431 wxString::Format(
"Pad %s from Footprint %s has net code %s and "
432 "is connected to zone with net code %s",
434 pad->GetParentFootprint()->GetReferenceAsString(),
436 zone->GetNetname() ) );
466 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } } guard{ cfg.
m_ZoneFillIterativeRefill, originalIterativeRefill };
473 ZONE* gndZone =
nullptr;
475 for(
ZONE* zone : m_board->Zones() )
477 if( zone->GetNetname() ==
"GND" )
484 BOOST_REQUIRE_MESSAGE( gndZone !=
nullptr,
"GND zone not found in test board" );
488 bool hasOutline = m_board->GetBoardPolygonOutlines( boardOutline,
true );
489 BOOST_REQUIRE_MESSAGE( hasOutline,
"Board outline not found" );
491 double boardArea = 0.0;
502 double fillRatio = gndFilledArea / boardArea;
504 BOOST_TEST_MESSAGE( wxString::Format(
"Board area: %.2f sq mm, GND filled area: %.2f sq mm, "
505 "Fill ratio: %.1f%%",
506 boardArea / 1e6, gndFilledArea / 1e6,
507 fillRatio * 100.0 ) );
510 wxString::Format(
"GND zone fill ratio %.1f%% is less than expected 25%%. "
511 "This indicates issue 21746 - lower priority zones not "
512 "filling areas where higher priority isolated islands "
514 fillRatio * 100.0 ) );
539 int viasWithUnreachableFlashing = 0;
540 int totalConditionalVias = 0;
542 PCB_LAYER_ID in1Cu = m_board->GetLayerID( wxT(
"In1.Cu" ) );
543 PCB_LAYER_ID in2Cu = m_board->GetLayerID( wxT(
"In2.Cu" ) );
545 for(
PCB_TRACK* track : m_board->Tracks() )
552 if( !
via->GetRemoveUnconnected() )
555 totalConditionalVias++;
558 bool flashedOnIn1 =
via->FlashLayer( in1Cu );
559 bool flashedOnIn2 =
via->FlashLayer( in2Cu );
561 if( !flashedOnIn1 && !flashedOnIn2 )
565 int holeRadius =
via->GetDrillValue() / 2;
568 bool zoneReachesVia =
false;
570 for(
ZONE* zone : m_board->Zones() )
572 if( zone->GetIsRuleArea() )
575 if( zone->GetNetCode() !=
via->GetNetCode() )
580 if( !zone->IsOnLayer( layer ) )
583 if( !zone->HasFilledPolysForLayer( layer ) )
586 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
588 if( fill->Contains( viaCenter, -1, holeRadius ) )
590 zoneReachesVia =
true;
600 if( !zoneReachesVia && ( flashedOnIn1 || flashedOnIn2 ) )
601 viasWithUnreachableFlashing++;
604 BOOST_TEST_MESSAGE( wxString::Format(
"Total conditional vias: %d, Vias with unreachable "
605 "flashing: %d", totalConditionalVias,
606 viasWithUnreachableFlashing ) );
609 wxString::Format(
"Found %d vias flashed on zone layers where the zone "
610 "fill doesn't actually reach them. This indicates "
611 "issue 22010 is not fixed.",
612 viasWithUnreachableFlashing ) );
634 int viasShortingZones = 0;
635 int totalConditionalVias = 0;
637 for(
PCB_TRACK* track : m_board->Tracks() )
644 if( !
via->GetRemoveUnconnected() )
647 totalConditionalVias++;
651 for(
ZONE* zone : m_board->Zones() )
653 if( zone->GetIsRuleArea() )
656 if( zone->GetNetCode() ==
via->GetNetCode() )
661 if( !
via->FlashLayer( layer ) )
664 if( !zone->HasFilledPolysForLayer( layer ) )
667 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
668 int viaRadius =
via->GetWidth( layer ) / 2;
670 if( fill->Contains( viaCenter, -1, viaRadius ) )
673 "Via at (%d, %d) on net %s is flashing on layer %s where zone "
674 "net %s is filled - this creates a short!",
675 viaCenter.
x, viaCenter.
y,
via->GetNetname(),
676 m_board->GetLayerName( layer ), zone->GetNetname() ) );
683 BOOST_TEST_MESSAGE( wxString::Format(
"Total conditional vias: %d, Vias shorting zones: %d",
684 totalConditionalVias, viasShortingZones ) );
687 wxString::Format(
"Found %d vias flashed on layers where they short to "
688 "zones with different nets. This indicates issue 12964 "
690 viasShortingZones ) );
708 KI_TEST::LoadBoard( m_settingsManager,
"hatch_thermal_connectivity/hatch_thermal_connectivity",
713 m_board->BuildConnectivity();
715 int unconnectedCount = m_board->GetConnectivity()->GetUnconnectedCount(
false );
718 wxString::Format(
"Found %d unconnected items after zone fill. "
719 "Hatch zone thermal reliefs should maintain connectivity "
720 "even with large hatch gaps.",
721 unconnectedCount ) );
745 PCB_LAYER_ID in1Cu = m_board->GetLayerID( wxT(
"In1.Cu" ) );
747 ZONE* gndZone =
nullptr;
749 for(
ZONE* zone : m_board->Zones() )
751 if( zone->GetNetname() ==
"GND" && zone->IsOnLayer( in1Cu ) )
758 BOOST_REQUIRE_MESSAGE( gndZone !=
nullptr,
"GND zone on In1.Cu not found in test board" );
766 "GND zone has no fill on In1.Cu" );
774 double zoneOutlineArea = gndZone->
Outline()->
Area();
776 BOOST_REQUIRE_MESSAGE( zoneOutlineArea > 0.0,
"Zone outline area must be positive" );
778 double fillArea = 0.0;
780 for(
int i = 0; i < fill->OutlineCount(); i++ )
781 fillArea +=
std::abs( fill->Outline( i ).Area() );
783 double fillRatio = fillArea / zoneOutlineArea;
787 BOOST_CHECK_GE( fillRatio, 0.90 );
810 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
818 std::vector<ZONE*> keepouts;
820 for(
ZONE* zone : m_board->Zones() )
822 if( zone->GetIsRuleArea() && zone->GetDoNotAllowZoneFills() )
823 keepouts.push_back( zone );
826 BOOST_REQUIRE_MESSAGE( !keepouts.empty(),
"No zone keepouts found in test board" );
829 int violationCount = 0;
831 for(
ZONE* keepout : keepouts )
833 for(
PCB_LAYER_ID layer : keepout->GetLayerSet().Seq() )
838 for(
ZONE* zone : m_board->Zones() )
840 if( zone->GetIsRuleArea() )
843 if( !zone->IsOnLayer( layer ) )
846 if( !zone->HasFilledPolysForLayer( layer ) )
849 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
857 double intersectionArea = 0;
863 if( intersectionArea > 1e6 )
866 "Zone %s fill on layer %s overlaps keepout by %.2f sq mm",
868 m_board->GetLayerName( layer ),
869 intersectionArea / 1e6 ) );
878 wxString::Format(
"Found %d zone fills overlapping keepout areas. "
879 "This indicates issue 22809 - iterative refiller "
880 "ignores zone keepouts.", violationCount ) );
903 PCB_LAYER_ID in2Cu = m_board->GetLayerID( wxT(
"In2.Cu" ) );
904 int padsWithMissingFlashing = 0;
905 int totalConditionalPads = 0;
907 for(
FOOTPRINT* footprint : m_board->Footprints() )
909 for(
PAD*
pad : footprint->Pads() )
911 if( !
pad->GetRemoveUnconnected() )
914 if( !
pad->HasHole() )
917 if(
pad->GetNetname() !=
"VBUS_DUT" &&
pad->GetNetname() !=
"VBUS_DBG" )
920 totalConditionalPads++;
923 bool shouldFlash =
false;
925 for(
ZONE* zone : m_board->Zones() )
927 if( zone->GetIsRuleArea() )
930 if( zone->GetNetCode() !=
pad->GetNetCode() )
933 if( !zone->IsOnLayer( in2Cu ) )
936 if( zone->Outline()->Contains(
pad->GetPosition() ) )
943 if( shouldFlash && !
pad->FlashLayer( in2Cu ) )
946 "Pad %s at (%d, %d) on net %s is inside zone but not flashing on In2.Cu",
947 pad->GetNumber(),
pad->GetPosition().x,
pad->GetPosition().y,
948 pad->GetNetname() ) );
949 padsWithMissingFlashing++;
954 BOOST_TEST_MESSAGE( wxString::Format(
"Total conditional pads: %d, Pads with missing "
955 "flashing: %d", totalConditionalPads,
956 padsWithMissingFlashing ) );
959 wxString::Format(
"Found %d TH pads that should flash on inner layers "
960 "but don't. This indicates issue 22826 is not fixed.",
961 padsWithMissingFlashing ) );
979 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
989 if( !commit.
Empty() )
993 int teardropCount = 0;
994 bool foundBadTeardrop =
false;
996 for(
ZONE* zone : m_board->Zones() )
998 if( !zone->IsTeardropArea() )
1015 int concaveCount = 0;
1017 for(
int i = 0; i <
chain.PointCount(); i++ )
1019 int prev = ( i == 0 ) ?
chain.PointCount() - 1 : i - 1;
1020 int next = ( i + 1 ) %
chain.PointCount();
1026 int64_t cross = (int64_t)
v1.x *
v2.y - (int64_t)
v1.y *
v2.x;
1036 if( concaveCount > 5 )
1039 "indicating possible corner intersection",
1041 foundBadTeardrop =
true;
1048 "Found teardrop with excessive concave vertices, indicating "
1049 "issue 19405 - teardrop curve intersecting rounded rectangle corner" );
1066 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1076 if( !commit.
Empty() )
1080 int teardropCount = 0;
1081 bool foundBadTeardrop =
false;
1083 for(
ZONE* zone : m_board->Zones() )
1085 if( !zone->IsTeardropArea() )
1099 int concaveCount = 0;
1101 for(
int i = 0; i <
chain.PointCount(); i++ )
1103 int prev = ( i == 0 ) ?
chain.PointCount() - 1 : i - 1;
1104 int next = ( i + 1 ) %
chain.PointCount();
1109 int64_t cross = (int64_t)
v1.x *
v2.y - (int64_t)
v1.y *
v2.x;
1115 if( concaveCount > 5 )
1119 foundBadTeardrop =
true;
1126 "Found teardrop with excessive concave vertices on oval pad, "
1127 "indicating curve is not tangent to semicircular end" );
1144 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1154 if( !commit.
Empty() )
1158 PAD* largePad =
nullptr;
1160 for(
FOOTPRINT* fp : m_board->Footprints() )
1162 for(
PAD*
pad : fp->Pads() )
1172 BOOST_REQUIRE_MESSAGE( largePad !=
nullptr,
"Expected a circular pad in test board" );
1178 int teardropCount = 0;
1179 bool foundBadTeardrop =
false;
1181 for(
ZONE* zone : m_board->Zones() )
1183 if( !zone->IsTeardropArea() )
1196 int concaveCount = 0;
1198 for(
int i = 0; i <
chain.PointCount(); i++ )
1200 int prev = ( i == 0 ) ?
chain.PointCount() - 1 : i - 1;
1201 int next = ( i + 1 ) %
chain.PointCount();
1206 int64_t cross = (int64_t)
v1.x *
v2.y - (int64_t)
v1.y *
v2.x;
1212 if( concaveCount > 5 )
1214 BOOST_TEST_MESSAGE( wxString::Format(
"Large circle teardrop has %d concave vertices",
1216 foundBadTeardrop =
true;
1221 int maxError = m_board->GetDesignSettings().m_MaxError;
1223 for(
int i = 0; i <
chain.PointCount(); i++ )
1226 double dist = ( pt - padCenter ).EuclideanNorm();
1229 if( dist > padRadius * 0.5 && dist < padRadius * 1.5 )
1231 double deviation =
std::abs( dist - padRadius );
1234 if( deviation > maxError * 5 && deviation < padRadius * 0.2 )
1237 "Teardrop point at distance %.2f from pad center (radius %.2f), "
1238 "deviation %.2f exceeds tolerance",
1239 dist / 1000.0, padRadius / 1000.0, deviation / 1000.0 ) );
1248 "Found teardrop with excessive concave vertices on large circle, "
1249 "indicating anchor points may not be on circle edge" );
1273 for(
ZONE* zone : m_board->Zones() )
1275 if( zone->GetIsRuleArea() )
1280 if( !zone->HasFilledPolysForLayer( layer ) )
1283 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1285 for(
PAD*
pad : m_board->GetPads() )
1287 if( !
pad->IsOnLayer( layer ) )
1290 if(
pad->GetNetCode() == zone->GetNetCode() )
1293 std::shared_ptr<SHAPE> padShape =
pad->GetEffectiveShape( layer );
1294 int clearance = padShape->GetClearance( fill.get() );
1299 "Pad %s (net %s) at (%d, %d) has zero clearance to zone %s "
1301 pad->GetNumber(),
pad->GetNetname(),
1302 pad->GetPosition().x,
pad->GetPosition().y,
1303 zone->GetNetname(), m_board->GetLayerName( layer ) ) );
1311 wxString::Format(
"Found %d pads with missing zone clearance. "
1312 "Coincident pads with different nets must not be "
1313 "deduplicated in zone fill knockout.",
1335 std::vector<DRC_ITEM> violations;
1340 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
1341 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
1345 BOARD_ITEM* item_a = m_board->ResolveItem( aItem->GetMainItemID() );
1346 BOARD_ITEM* item_b = m_board->ResolveItem( aItem->GetAuxItemID() );
1348 ZONE* zone_a =
dynamic_cast<ZONE*
>( item_a );
1349 ZONE* zone_b =
dynamic_cast<ZONE*
>( item_b );
1351 if( zone_a || zone_b )
1352 violations.push_back( *aItem );
1379 ZONE* hvZone =
nullptr;
1380 ZONE* lvZone =
nullptr;
1382 for(
ZONE* zone : m_board->Zones() )
1384 if( zone->GetNetname() ==
"HV_NET" )
1386 else if( zone->GetNetname() ==
"LV_NET" )
1395 hvZone, lvZone,
F_Cu );
1398 <<
" (expected " <<
pcbIUScale.mmToIU( 4.6 ) <<
")" );
1403 hvZone, lvZone,
In1_Cu );
1406 <<
" (expected " <<
pcbIUScale.mmToIU( 2.3 ) <<
")" );
1413 std::vector<DRC_ITEM> violations;
1418 [&](
const std::shared_ptr<DRC_ITEM>& aItem,
const VECTOR2I& aPos,
int aLayer,
1419 const std::function<
void(
PCB_MARKER* )>& aPathGenerator )
1423 BOARD_ITEM* item_a = m_board->ResolveItem( aItem->GetMainItemID() );
1424 BOARD_ITEM* item_b = m_board->ResolveItem( aItem->GetAuxItemID() );
1426 ZONE* zone_a =
dynamic_cast<ZONE*
>( item_a );
1427 ZONE* zone_b =
dynamic_cast<ZONE*
>( item_b );
1429 if( zone_a && zone_b )
1432 << aLayer <<
": " << aItem->GetErrorMessage(
true ) );
1433 violations.push_back( *aItem );
1446 KI_TEST::LoadBoard( m_settingsManager,
"issue23332_min_width/issue23332_min_width", m_board );
1452 for(
ZONE* zone : m_board->Zones() )
1454 int half_min_width = zone->GetMinThickness() / 2;
1461 if( !zone->HasFilledPolysForLayer( layer ) )
1464 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1466 if( !fill || fill->OutlineCount() == 0 )
1471 for(
int ii = 0; ii < fill->OutlineCount(); ii++ )
1476 for(
int jj = 0; jj < fill->HoleCount( ii ); jj++ )
1477 island.
AddHole( fill->Hole( ii, jj ) );
1479 double originalArea = island.
Area();
1481 if( originalArea <= 0 )
1492 double prunedArea =
test.Area();
1493 double areaLoss = ( originalArea - prunedArea ) / originalArea;
1496 "Zone %s layer %d island %d: area=%.0f, loss=%.4f%%",
1497 zone->GetNetname(),
static_cast<int>( layer ), ii,
1498 originalArea, areaLoss * 100.0 ) );
1502 "Zone %s layer %d island %d lost %.2f%% area from "
1503 "min-width pruning (min_width=%.3fmm)",
1504 zone->GetNetname(),
static_cast<int>( layer ), ii,
1506 zone->GetMinThickness()
1507 /
static_cast<double>(
pcbIUScale.IU_PER_MM ) ) );
1522 for(
ZONE* zone : m_board->Zones() )
1524 int half_min_width = zone->GetMinThickness() / 2;
1531 if( !zone->HasFilledPolysForLayer( layer ) )
1534 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1536 if( !fill || fill->OutlineCount() == 0 )
1539 for(
int ii = 0; ii < fill->OutlineCount(); ii++ )
1544 for(
int jj = 0; jj < fill->HoleCount( ii ); jj++ )
1545 island.
AddHole( fill->Hole( ii, jj ) );
1547 double originalArea = island.
Area();
1549 if( originalArea <= 0 )
1560 double prunedArea =
test.Area();
1561 double areaLoss = ( originalArea - prunedArea ) / originalArea;
1565 "Zone %s (priority %d) layer %d island %d lost "
1566 "%.2f%% area from min-width pruning, suggesting "
1567 "degenerate geometry from overlapping same-net zones",
1569 zone->GetAssignedPriority(),
1570 static_cast<int>( layer ), ii,
1571 areaLoss * 100.0 ) );
1583 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
1586 auto runAreaLossCheck =
1587 [
this](
bool aIterative )
1597 for(
ZONE* zone : m_board->Zones() )
1599 int half_min_width = zone->GetMinThickness() / 2;
1606 if( !zone->HasFilledPolysForLayer( layer ) )
1609 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
1611 if( !fill || fill->OutlineCount() == 0 )
1614 for(
int ii = 0; ii < fill->OutlineCount(); ii++ )
1619 for(
int jj = 0; jj < fill->HoleCount( ii ); jj++ )
1620 island.
AddHole( fill->Hole( ii, jj ) );
1622 double originalArea = island.
Area();
1624 if( originalArea <= 0 )
1635 double prunedArea =
test.Area();
1636 double areaLoss = ( originalArea - prunedArea ) / originalArea;
1641 "Zone %s (priority %d) layer %d island %d lost "
1642 "%.2f%% area (iterative=%d), suggesting degenerate "
1643 "geometry from different-net zone knockouts",
1644 zone->GetNetname(), zone->GetAssignedPriority(),
1645 static_cast<int>( layer ), ii, areaLoss * 100.0,
1652 runAreaLossCheck(
false );
1653 runAreaLossCheck(
true );
1678 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
1681 KI_TEST::LoadBoard( m_settingsManager,
"issue23535_minimal/issue23535_minimal", m_board );
1685 ZONE* gndZone =
nullptr;
1687 for(
ZONE* zone : m_board->Zones() )
1689 if( zone->GetNetname() ==
"GND" )
1707 bool hasSpokeToNowhere = gndFill->Contains( spokeTestPoint );
1710 "GND zone fill contains copper at the thermal gap test point (7.4, 5.0), "
1711 "indicating a thermal relief spoke to nowhere (issue 23535)." );
1718 bool hasValidSpoke = gndFill->Contains( validSpokePoint );
1721 "GND zone fill does not contain copper at the valid spoke test point "
1722 "(5.6, 5.0). The fix may have incorrectly removed valid spokes." );
1740 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1749 if( !commit.
Empty() )
1757 int teardropCount = 0;
1759 for(
ZONE* zone : m_board->Zones() )
1761 if( !zone->IsTeardropArea() )
1779 for(
int i = 0; i <
chain.PointCount(); i++ )
1781 int dy =
chain.CPoint( i ).y - trackY;
1784 maxAbove = std::max( maxAbove, -dy );
1786 maxBelow = std::max( maxBelow, dy );
1791 "Teardrop should extend on both sides of the track axis" );
1793 if( maxAbove > 0 && maxBelow > 0 )
1797 double ratio =
static_cast<double>( std::min( maxAbove, maxBelow ) )
1798 /
static_cast<double>( std::max( maxAbove, maxBelow ) );
1801 wxString::Format(
"Teardrop asymmetry ratio %.2f is too low "
1802 "(above=%d, below=%d). Expected roughly "
1803 "symmetric about the track axis.",
1804 ratio, maxAbove, maxBelow ) );
1808 BOOST_CHECK_MESSAGE( teardropCount > 0,
"Expected at least one teardrop zone for off-center track" );
1825 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1834 if( !commit.
Empty() )
1840 PAD* testPad =
nullptr;
1842 for(
FOOTPRINT* fp : m_board->Footprints() )
1844 for(
PAD*
pad : fp->Pads() )
1846 if(
pad->GetNumber() ==
"7" )
1854 BOOST_REQUIRE_MESSAGE( testPad !=
nullptr,
"Could not find pad 7 in test board" );
1857 int tolerance = std::max( m_board->GetDesignSettings().m_MaxError,
1863 int teardropCount = 0;
1865 for(
ZONE* zone : m_board->Zones() )
1867 if( !zone->IsTeardropArea() )
1872 BOOST_REQUIRE_MESSAGE( outline && outline->
OutlineCount() > 0,
1873 "Teardrop zone has no outline" );
1887 for(
int i = 0; i <
chain.PointCount(); i++ )
1890 double distToPad = (
VECTOR2D( pt ) -
VECTOR2D( padCenter ) ).EuclideanNorm();
1891 double distToTrack = (
VECTOR2D( pt ) -
VECTOR2D( trackAnchor ) ).EuclideanNorm();
1894 if( distToPad < distToTrack )
1898 wxString::Format(
"Teardrop vertex (%d, %d) is outside the pad "
1899 "outline with %d nm tolerance",
1900 pt.
x, pt.
y, tolerance ) );
1906 "Expected at least one teardrop zone for elongated pad" );
1920 auto runVariant = [&](
bool aCurvedEdges )
1924 for(
PCB_TRACK* track : m_board->Tracks() )
1928 static_cast<PCB_VIA*
>( track )->SetTeardropCurved( aCurvedEdges );
1934 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
1943 if( !commit.
Empty() )
1946 int teardropCount = 0;
1947 bool foundSelfIntersection =
false;
1949 for(
ZONE* zone : m_board->Zones() )
1951 if( !zone->IsTeardropArea() )
1962 int n =
chain.PointCount();
1964 for(
int i = 0; i < n && !foundSelfIntersection; i++ )
1966 SEG segA(
chain.CPoint( i ),
chain.CPoint( ( i + 1 ) % n ) );
1968 for(
int j = i + 2; j < n; j++ )
1970 if( i == 0 && j == n - 1 )
1973 SEG segB(
chain.CPoint( j ),
chain.CPoint( ( j + 1 ) % n ) );
1976 if( hit.has_value() )
1979 "Self-intersection at (%d, %d) between edges %d and %d "
1981 hit->x, hit->y, i, j,
1982 aCurvedEdges ?
"yes" :
"no" ) );
1984 for(
int k = 0; k < n; k++ )
1987 " pt[%d] = (%d, %d)", k,
1988 chain.CPoint( k ).x,
chain.CPoint( k ).y ) );
1991 foundSelfIntersection =
true;
1999 wxString::Format(
"Expected at least one teardrop zone "
2001 aCurvedEdges ?
"yes" :
"no" ) );
2004 wxString::Format(
"Teardrop polygon has self-intersecting "
2005 "edges (curved=%s)",
2006 aCurvedEdges ?
"yes" :
"no" ) );
2010 runVariant(
false );
2034 auto runVariant = [&](
bool aCurvedEdges )
2041 for(
PCB_TRACK* track : m_board->Tracks() )
2046 via->SetTeardropCurved( aCurvedEdges );
2047 viaPos =
via->GetPosition();
2056 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2065 if( !commit.
Empty() )
2070 const double maxBackSideDist = viaRadius * 1.2;
2071 int teardropCount = 0;
2072 int spikingPoints = 0;
2074 double worstDistance = 0.0;
2076 for(
ZONE* zone : m_board->Zones() )
2078 if( !zone->IsTeardropArea() )
2090 for(
int i = 0; i <
chain.PointCount(); i++ )
2101 if( dist > maxBackSideDist )
2105 if( dist > worstDistance )
2107 worstDistance = dist;
2115 wxString::Format(
"Expected no teardrop on grazing-entry "
2116 "track (emergence below track width), got "
2119 aCurvedEdges ?
"yes" :
"no" ) );
2122 wxString::Format(
"Found %d teardrop polygon vertex/vertices "
2123 "outside the expected envelope (worst at "
2124 "(%d, %d), %f mm from via center; curved=%s)",
2126 worstPoint.
x, worstPoint.
y,
2128 aCurvedEdges ?
"yes" :
"no" ) );
2132 runVariant(
false );
2150 auto runVariant = [&](
bool aCurvedEdges )
2154 for(
PCB_TRACK* track : m_board->Tracks() )
2158 static_cast<PCB_VIA*
>( track )->SetTeardropCurved( aCurvedEdges );
2164 toolMgr.
SetEnvironment( m_board.get(),
nullptr,
nullptr,
nullptr,
nullptr );
2173 if( !commit.
Empty() )
2176 int teardropCount = 0;
2177 int selfIntersectingCount = 0;
2180 for(
ZONE* zone : m_board->Zones() )
2182 if( !zone->IsTeardropArea() )
2193 int n =
chain.PointCount();
2194 bool intersected =
false;
2196 for(
int i = 0; i < n && !intersected; i++ )
2198 SEG segA(
chain.CPoint( i ),
chain.CPoint( ( i + 1 ) % n ) );
2200 for(
int j = i + 2; j < n; j++ )
2202 if( i == 0 && j == n - 1 )
2205 SEG segB(
chain.CPoint( j ),
chain.CPoint( ( j + 1 ) % n ) );
2208 if( hit.has_value() )
2211 "Teardrop polygon self-intersection at (%d, %d) "
2212 "between edges %d and %d (curved=%s)",
2213 hit->x, hit->y, i, j, aCurvedEdges ?
"yes" :
"no" ) );
2215 worstPoint = hit.value();
2223 selfIntersectingCount++;
2227 wxString::Format(
"Expected at least one teardrop zone "
2229 aCurvedEdges ?
"yes" :
"no" ) );
2232 wxString::Format(
"%d of %d teardrop polygon(s) self-intersect "
2233 "(worst at (%d, %d); curved=%s)",
2234 selfIntersectingCount, teardropCount,
2235 worstPoint.
x, worstPoint.
y,
2236 aCurvedEdges ?
"yes" :
"no" ) );
2240 runVariant(
false );
2256 struct ScopeGuard {
bool& ref;
bool orig; ~ScopeGuard() { ref = orig; } }
2259 auto getTotalFilledArea =
2264 for(
ZONE* zone : m_board->Zones() )
2266 if( zone->GetIsRuleArea() )
2271 if( !zone->HasFilledPolysForLayer( layer ) )
2274 std::shared_ptr<SHAPE_POLY_SET> fill = zone->GetFilledPolysList( layer );
2284 auto refillAndMeasure =
2285 [
this, &cfg, &getTotalFilledArea](
bool aIterative ) ->
double
2291 double storedArea = getTotalFilledArea();
2293 BOOST_REQUIRE_MESSAGE( storedArea > 0,
"Stored v9 fill has zero area" );
2296 return getTotalFilledArea();
2301 double storedArea = getTotalFilledArea();
2303 BOOST_REQUIRE_MESSAGE( storedArea > 0,
"Stored v9 fill has zero area" );
2305 double nonIterativeArea = refillAndMeasure(
false );
2306 double iterativeArea = refillAndMeasure(
true );
2307 double nonIterativeAreaRatio = nonIterativeArea / storedArea;
2308 double iterativeAreaRatio = iterativeArea / storedArea;
2311 nonIterativeAreaRatio > 0.99999,
2313 "Non-iterative refill lost %.4f%% versus stored v9 fill "
2314 "(stored=%.2f mm^2, non-iterative=%.2f mm^2). "
2315 "This suggests missing pieces near keepout boundaries (issue 23515).",
2316 ( 1.0 - nonIterativeAreaRatio ) * 100.0,
2317 storedArea / 1e6, nonIterativeArea / 1e6 ) );
2320 iterativeAreaRatio > 0.99999,
2322 "Iterative refill lost %.4f%% versus stored v9 fill "
2323 "(stored=%.2f mm^2, iterative=%.2f mm^2). "
2324 "This suggests missing pieces near keepout boundaries (issue 23515).",
2325 ( 1.0 - iterativeAreaRatio ) * 100.0,
2326 storedArea / 1e6, iterativeArea / 1e6 ) );
2340 m_board = std::make_unique<BOARD>();
2343 m_board->SetCopperLayerCount( 2 );
2352 m_board->Add( gndNet );
2364 int hatchThickness =
pcbIUScale.mmToIU( 0.3 );
2373 via->SetPosition( viaPos );
2375 via->SetDrill( viaDrill );
2377 via->SetNetCode( gndNetCode );
2378 m_board->Add(
via );
2382 auto makeHatchZone =
2385 ZONE* zone =
new ZONE( m_board.get() );
2404 m_board->Add( zone );
2411 m_board->BuildConnectivity();
2412 auto drcEngine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
2413 drcEngine->InitEngine( wxFileName() );
2425 double fullFillArea = 0.0;
2426 double thermalFillArea = 0.0;
2437 "Zone should have fill on F.Cu with FULL connection" );
2441 for(
int i = 0; i < fill->OutlineCount(); i++ )
2442 fullFillArea +=
std::abs( fill->Outline( i ).Area() );
2444 m_board->Remove(
via );
2445 m_board->Remove( zone );
2459 "Zone should have fill on F.Cu with THERMAL connection" );
2463 for(
int i = 0; i < fill->OutlineCount(); i++ )
2464 thermalFillArea +=
std::abs( fill->Outline( i ).Area() );
2466 m_board->Remove(
via );
2467 m_board->Remove( zone );
2476 double areaThreshold = 0.2 * iuPerMM * iuPerMM;
2478 double areaIU2toMM2 = 1.0 / ( iuPerMM * iuPerMM );
2482 "THERMAL connection fill area (%.2f sq mm) should be larger "
2483 "than FULL fill area (%.2f sq mm) by at least 0.2 sq mm. "
2484 "If they are equal or FULL is larger, thermal ring was not "
2485 "added for THERMAL connection, or thermal ring was incorrectly "
2486 "added for FULL connection (issue 23516 regression).",
2487 thermalFillArea * areaIU2toMM2, fullFillArea * areaIU2toMM2 ) );
2518 ~ScopeGuard() { ref = orig; }
2524 const std::vector<std::string> checkedNames = {
"hi1",
"hi2",
"hi3",
"hi4",
"hi5",
"hi6",
"hi7",
2525 "lo1",
"lo2",
"lo3",
"lo4",
"lo5",
"lo6" };
2526 std::map<std::string, ZONE*> zoneByName;
2528 for(
ZONE* zone : m_board->Zones() )
2529 zoneByName[zone->GetZoneName().ToStdString()] = zone;
2531 for(
const std::string&
name : checkedNames )
2533 BOOST_REQUIRE_MESSAGE( zoneByName.count(
name ),
"Zone '" +
name +
"' not found in test board" );
2534 BOOST_REQUIRE_MESSAGE( zoneByName[
name]->HasFilledPolysForLayer(
F_Cu ),
2535 "Zone '" +
name +
"' has no fill on F.Cu" );
2539 for(
const std::string&
name : {
"hi3",
"hi5",
"hi7" } )
2541 int islands = zoneByName[
name]->GetFilledPolysList(
F_Cu )->OutlineCount();
2543 BOOST_CHECK_MESSAGE( islands == 2, wxString::Format(
"Zone '%s' should have 2 filled islands but has %d. "
2544 "Cascading island removal did not converge correctly.",
2549 for(
const std::string&
name : {
"lo1",
"lo2",
"lo3",
"lo4",
"lo5",
"lo6",
"hi2",
"hi4",
"hi6" } )
2551 int islands = zoneByName[
name]->GetFilledPolysList(
F_Cu )->OutlineCount();
2553 BOOST_CHECK_MESSAGE( islands == 1, wxString::Format(
"Zone '%s' should have 1 filled island but has %d. "
2554 "Iterative refill may have incorrectly blocked or "
2555 "expanded this zone.",
2572 ZONE* thievingZone =
nullptr;
2574 for(
ZONE* z : m_board->Zones() )
2591 BOOST_CHECK_GE( fill->OutlineCount(), 2 );
2594 BOX2I fillBox = fill->BBox();
2601 BOOST_CHECK_GT( fill->TotalVertices(), 200 );
2627 ~ScopeGuard() { ref = orig; }
2631 class WarningCapture :
public wxLog
2634 bool m_hadWarning =
false;
2637 void DoLogRecord( wxLogLevel aLevel,
const wxString&,
const wxLogRecordInfo& )
override
2639 if( aLevel == wxLOG_Warning )
2640 m_hadWarning =
true;
2644 auto* capture =
new WarningCapture();
2645 wxLog* oldLog = wxLog::SetActiveTarget( capture );
2650 ~LogGuard() { wxLog::SetActiveTarget( old ); }
2651 } logGuard{ oldLog };
2656 BOOST_CHECK_MESSAGE( capture->m_hadWarning,
"Expected a wxLogWarning when iterative refill hits the iteration "
2657 "limit, but none was emitted. The convergence-limit board may no "
2658 "longer trigger the cap, or the warning path has changed." );
2669 m_board = std::make_unique<BOARD>();
2671 ZONE* zone =
new ZONE( m_board.get() );
2685 m_board->Add( zone );
2694 BOOST_CHECK_GT( fill->OutlineCount(), 5 );
2707 m_board = std::make_unique<BOARD>();
2708 m_board->SetCopperLayerCount( 2 );
2711 ZONE* zone =
new ZONE( m_board.get() );
2731 m_board->Add( zone );
2737 BOOST_REQUIRE_GT( fill->OutlineCount(), 0 );
2741 BOOST_CHECK_GE( fill->OutlineCount(), 6 );
2742 BOOST_CHECK_LE( fill->OutlineCount(), 12 );
2745 const double fullDotArea =
M_PI * std::pow(
pcbIUScale.mmToIU( 0.25 ), 2 );
2758 auto countDots = [](
bool stagger ) ->
int
2760 auto board = std::make_unique<BOARD>();
2761 board->SetCopperLayerCount( 2 );
2763 ZONE* zone =
new ZONE( board.get() );
2784 int plain = countDots(
false );
2785 int staggered = countDots(
true );
2791 BOOST_CHECK_NE( plain, staggered );
2792 BOOST_CHECK_GE( staggered, plain / 2 );
2793 BOOST_CHECK_LE( staggered, plain * 2 );
2804 m_board = std::make_unique<BOARD>();
2805 m_board->SetCopperLayerCount( 2 );
2807 ZONE* zone =
new ZONE( m_board.get() );
2821 m_board->Add( zone );
2827 BOOST_REQUIRE_GT( fill->OutlineCount(), 0 );
2831 BOOST_CHECK_GE( fill->OutlineCount(), 6 );
2832 BOOST_CHECK_LE( fill->OutlineCount(), 12 );
2834 const double fullSquareArea = std::pow(
pcbIUScale.mmToIU( 0.6 ), 2 );
2849 m_board = std::make_unique<BOARD>();
2850 m_board->SetCopperLayerCount( 2 );
2852 ZONE* zone =
new ZONE( m_board.get() );
2866 m_board->Add( zone );
2868 auto start = std::chrono::steady_clock::now();
2870 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
2871 std::chrono::steady_clock::now() - start )
2881 BOOST_CHECK_LT( elapsed, 30000 );
2893 m_board = std::make_unique<BOARD>();
2894 m_board->SetCopperLayerCount( 2 );
2896 ZONE* zone =
new ZONE( m_board.get() );
2910 m_board->Add( zone );
2916 BOOST_REQUIRE_GT( fill->TotalVertices(), 0 );
2926 BOX2I fillBox = fill->BBox();
2934 BOOST_CHECK_GT( fill->TotalVertices(), 30 );
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< 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.
Handle the data for a net.
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.
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
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.
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)
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 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
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.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
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 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 SetPadConnection(ZONE_CONNECTION aPadConnection)
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
void SetHatchGap(int aStep)
@ 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)
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())
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
const SHAPE_LINE_CHAIN chain
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