30#include <unordered_set>
74 RESULTS(
int aOutline1,
int aOutline2,
int aVertex1,
int aVertex2 ) :
121 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
124 auto check_pt = [&](
VERTEX* p )
126 VECTOR2D diff( p->x - aPt->
x, p->y - aPt->
y );
129 if( dist2 > 0 && dist2 < limit2 && dist2 < min_dist && p->isEar(
true ) )
138 while( p && p->
z <= maxZ )
146 while( p && p->
z >= minZ )
161 std::set<VERTEX*> visited;
174 if( ( visited.empty() || !visited.contains( p ) ) && ( q =
getPoint( p ) ) )
178 if( !visited.contains( q ) &&
180 p->
i, q->
i ).second )
184 visited.insert( p->
prev );
186 visited.insert( p->
next );
189 visited.insert( q->
prev );
191 visited.insert( q->
next );
226struct PAD_KNOCKOUT_KEY
234 bool operator==(
const PAD_KNOCKOUT_KEY& other )
const
236 return position == other.position && effectiveSize == other.effectiveSize
237 && shape == other.shape && orientation == other.orientation
238 && netCode == other.netCode;
242struct PAD_KNOCKOUT_KEY_HASH
244 size_t operator()(
const PAD_KNOCKOUT_KEY& key )
const
246 return hash_val( key.position.
x, key.position.
y, key.effectiveSize.
x, key.effectiveSize.
y,
247 key.shape, key.orientation.
AsDegrees(), key.netCode );
254struct VIA_KNOCKOUT_KEY
260 bool operator==(
const VIA_KNOCKOUT_KEY& other )
const
262 return position == other.position && effectiveSize == other.effectiveSize
263 && netCode == other.netCode;
267struct VIA_KNOCKOUT_KEY_HASH
269 size_t operator()(
const VIA_KNOCKOUT_KEY& key )
const
271 return hash_val( key.position.
x, key.position.
y, key.effectiveSize, key.netCode );
277struct TRACK_KNOCKOUT_KEY
283 TRACK_KNOCKOUT_KEY(
const VECTOR2I& aStart,
const VECTOR2I& aEnd,
int aWidth ) :
287 if( aStart.
x < aEnd.
x || ( aStart.
x == aEnd.
x && aStart.
y <= aEnd.
y ) )
299 bool operator==(
const TRACK_KNOCKOUT_KEY& other )
const
301 return start == other.start && end == other.end && width == other.width;
305struct TRACK_KNOCKOUT_KEY_HASH
307 size_t operator()(
const TRACK_KNOCKOUT_KEY& key )
const
309 return hash_val( key.start.
x, key.start.
y, key.end.
x, key.end.
y, key.width );
353 std::lock_guard<KISPINLOCK> lock(
m_board->GetConnectivity()->GetLock() );
355 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
356 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
HASH_128> oldFillHashes;
357 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
359 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
m_board->GetConnectivity();
366 connectivity->ClearRatsnest();
374 :
_(
"Building zone fills..." ) );
387 zone->CacheBoundingBox();
391 for(
PAD*
pad : footprint->Pads() )
395 pad->BuildEffectiveShapes();
400 for(
ZONE* zone : footprint->Zones() )
401 zone->CacheBoundingBox();
404 footprint->BuildCourtyardCaches();
405 footprint->BuildNetTieCache();
410 auto findHighestPriorityZone =
412 const std::function<bool(
const ZONE* )>& testFn ) ->
ZONE*
414 unsigned highestPriority = 0;
415 ZONE* highestPriorityZone =
nullptr;
420 if( zone->GetIsRuleArea() )
423 if( zone->GetAssignedPriority() < highestPriority )
426 if( !zone->IsOnLayer( itemLayer ) )
430 if( zone->GetNumCorners() <= 2 )
433 if( !zone->GetBoundingBox().Intersects( bbox ) )
436 if( !testFn( zone ) )
440 if( zone->GetAssignedPriority() > highestPriority
441 || zone->GetNetCode() == netcode )
443 highestPriority = zone->GetAssignedPriority();
444 highestPriorityZone = zone;
448 return highestPriorityZone;
451 auto isInPourKeepoutArea =
456 if( !zone->GetIsRuleArea() )
459 if( !zone->HasKeepoutParametersSet() )
462 if( !zone->GetDoNotAllowZoneFills() )
465 if( !zone->IsOnLayer( itemLayer ) )
469 if( zone->GetNumCorners() <= 2 )
472 if( !zone->GetBoundingBox().Intersects( bbox ) )
475 if( zone->Outline()->Contains( testPoint ) )
492 via->ClearZoneLayerOverrides();
494 if( !
via->GetRemoveUnconnected() )
499 int holeRadius =
via->GetDrillValue() / 2 + 1;
500 int netcode =
via->GetNetCode();
501 LSET layers =
via->GetLayerSet() & boardCuMask;
505 [&](
const ZONE* aZone ) ->
bool
507 return aZone->Outline()->Contains(
center, -1, holeRadius );
512 if( !
via->ConditionallyFlashed( layer ) )
515 if( isInPourKeepoutArea( bbox, layer,
center ) )
521 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
526 || layer == padstack.
Drill().
end ) )
542 for(
PAD*
pad : footprint->Pads() )
544 pad->ClearZoneLayerOverrides();
546 if( !
pad->GetRemoveUnconnected() )
551 int netcode =
pad->GetNetCode();
552 LSET layers =
pad->GetLayerSet() & boardCuMask;
555 [&](
const ZONE* aZone ) ->
bool
557 return aZone->Outline()->Contains(
center );
562 if( !
pad->ConditionallyFlashed( layer ) )
565 if( isInPourKeepoutArea( bbox, layer,
center ) )
571 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
582 for(
ZONE* zone : aZones )
585 if( zone->GetIsRuleArea() )
589 if( zone->GetNumCorners() <= 2 )
599 zone->BuildHashValue( layer );
600 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
603 toFill.emplace_back( std::make_pair( zone, layer ) );
612 auto check_fill_dependency =
620 if( aOtherZone->GetFillFlag( aLayer ) )
625 if( aOtherZone->GetIsRuleArea() )
629 if( aOtherZone->GetNumCorners() <= 2 )
633 if( !aOtherZone->GetLayerSet().test( aLayer ) )
640 if( aOtherZone->SameNet( aZone ) )
648 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
655 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
658 ZONE* zone = aFillItem.first;
663 for(
ZONE* otherZone : aZones )
665 if( otherZone == zone )
668 if( check_fill_dependency( zone, layer, otherZone ) )
683 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
685 if( !zoneLock.owns_lock() )
702 auto tesselate_lambda =
703 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
709 ZONE* zone = aFillItem.first;
712 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
714 if( !zoneLock.owns_lock() )
726 std::vector<std::pair<std::future<int>,
int>> returns;
727 returns.reserve( toFill.size() );
729 std::atomic<bool> cancelled(
false );
733 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
735 returns.emplace_back( std::make_pair(
tp.submit_task(
738 return fill_lambda( fillItem );
742 while( !cancelled && finished != 2 * toFill.size() )
744 for(
size_t ii = 0; ii < returns.size(); ++ii )
746 auto& ret = returns[ii];
751 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
753 if( status == std::future_status::ready )
755 if( ret.first.get() )
764 if( ret.second == 0 )
766 returns[ii].first =
tp.submit_task(
769 return fill_lambda( toFill[idx] );
772 else if( ret.second == 1 )
774 returns[ii].first =
tp.submit_task(
777 return tesselate_lambda( toFill[idx] );
784 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
798 for(
auto& ret : returns )
800 if( ret.first.valid() )
802 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
804 while( status != std::future_status::ready )
809 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
828 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
829 connectivity->SetProgressReporter(
nullptr );
834 for(
ZONE* zone : aZones )
837 if( zone->GetIsRuleArea() )
840 zone->SetIsFilled(
true );
847 std::set<ZONE*> zonesWithRemovedIslands;
849 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
852 bool allIslands =
true;
854 for(
const auto& [ layer, layerIslands ] : zoneIslands )
856 if( layerIslands.m_IsolatedOutlines.size()
857 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
867 for(
const auto& [ layer, layerIslands ] : zoneIslands )
872 if( layerIslands.m_IsolatedOutlines.empty() )
875 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
879 std::sort( islands.begin(), islands.end(), std::greater<int>() );
881 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
882 long long int minArea = zone->GetMinIslandArea();
885 wxLogTrace(
traceZoneFiller, wxT(
"Zone %s layer %d: %zu islands to process, mode=%d, poly has %d outlines, area %.0f" ),
886 zone->GetNetname(),
static_cast<int>( layer ), islands.size(),
887 static_cast<int>( mode ), poly->OutlineCount(), poly->Area() );
889 for(
int idx : islands )
895 wxLogTrace(
traceZoneFiller, wxT(
"Removing island %d from zone %s (ALWAYS mode)" ),
896 idx, zone->GetNetname() );
897 poly->DeletePolygonAndTriangulationData( idx,
false );
898 zonesWithRemovedIslands.insert( zone );
902 wxLogTrace(
traceZoneFiller, wxT(
"Removing island %d from zone %s (AREA mode, area=%.0f < min=%.0f)" ),
903 idx, zone->GetNetname(), outline.
Area(
true ),
904 static_cast<double>( minArea ) );
905 poly->DeletePolygonAndTriangulationData( idx,
false );
906 zonesWithRemovedIslands.insert( zone );
910 zone->SetIsIsland( layer, idx );
914 poly->UpdateTriangulationDataHash();
915 zone->CalculateFilledArea();
917 BOX2I bbox = poly->BBox();
918 wxLogTrace(
traceZoneFiller, wxT(
"After island removal, zone %s: %d outlines, area %.0f, bbox (%d,%d)-(%d,%d)" ),
919 zone->GetNetname(), poly->OutlineCount(), poly->Area(),
932 if( iterativeRefill && !zonesWithRemovedIslands.empty() )
936 wxLogTrace(
traceZoneFiller, wxT(
"Iterative refill: %zu zones had islands removed, cache size: %zu" ),
942 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zonesToRefill;
944 for(
ZONE* zoneWithIsland : zonesWithRemovedIslands )
946 BOX2I islandZoneBBox = zoneWithIsland->GetBoundingBox();
949 for(
ZONE* zone : aZones )
952 if( zone == zoneWithIsland )
956 if( zone->GetIsRuleArea() )
960 if( !zoneWithIsland->HigherPriority( zone ) )
964 LSET commonLayers = zone->GetLayerSet() & zoneWithIsland->GetLayerSet();
966 if( commonLayers.none() )
970 if( !zone->GetBoundingBox().Intersects( islandZoneBBox ) )
976 auto fillItem = std::make_pair( zone, layer );
978 if( std::find( zonesToRefill.begin(), zonesToRefill.end(), fillItem ) == zonesToRefill.end() )
980 zonesToRefill.push_back( fillItem );
986 if( !zonesToRefill.empty() )
988 wxLogTrace(
traceZoneFiller, wxT(
"Iterative refill: refilling %zu zone/layer pairs" ),
989 zonesToRefill.size() );
1000 auto cached_refill_lambda =
1001 [&](
const std::pair<ZONE*, PCB_LAYER_ID>& aFillItem ) ->
int
1003 ZONE* zone = aFillItem.first;
1010 wxLogTrace(
traceZoneFiller, wxT(
"Cached refill for zone %s: %d outlines, area %.0f" ),
1018 std::vector<std::pair<std::future<int>,
int>> refillReturns;
1019 refillReturns.reserve( zonesToRefill.size() );
1020 size_t refillFinished = 0;
1022 for(
const auto& fillItem : zonesToRefill )
1024 refillReturns.emplace_back( std::make_pair(
tp.submit_task(
1027 return cached_refill_lambda( fillItem );
1031 while( !cancelled && refillFinished != 2 * zonesToRefill.size() )
1033 for(
size_t ii = 0; ii < refillReturns.size(); ++ii )
1035 auto& ret = refillReturns[ii];
1037 if( ret.second > 1 )
1040 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
1042 if( status == std::future_status::ready )
1044 if( ret.first.get() )
1049 else if( ret.second == 0 )
1053 refillFinished += 2;
1060 if( ret.second == 1 )
1062 refillReturns[ii].first =
tp.submit_task(
1065 return tesselate_lambda( zonesToRefill[idx] );
1072 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1084 for(
auto& ret : refillReturns )
1086 if( ret.first.valid() )
1088 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
1090 while( status != std::future_status::ready )
1095 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
1101 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> refillIslandsMap;
1102 std::set<ZONE*> refillZones;
1104 for(
const auto& [zone, layer] : zonesToRefill )
1105 refillZones.insert( zone );
1107 for(
ZONE* zone : refillZones )
1109 refillIslandsMap[zone] = std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>();
1115 connectivity->FillIsolatedIslandsMap( refillIslandsMap );
1118 for(
const auto& [ zone, zoneIslands ] : refillIslandsMap )
1120 bool allIslands =
true;
1122 for(
const auto& [ layer, layerIslands ] : zoneIslands )
1124 if( layerIslands.m_IsolatedOutlines.size()
1125 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
1135 for(
const auto& [ layer, layerIslands ] : zoneIslands )
1140 if( layerIslands.m_IsolatedOutlines.empty() )
1143 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
1144 std::sort( islands.begin(), islands.end(), std::greater<int>() );
1146 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
1147 long long int minArea = zone->GetMinIslandArea();
1150 for(
int idx : islands )
1155 poly->DeletePolygonAndTriangulationData( idx,
false );
1157 poly->DeletePolygonAndTriangulationData( idx,
false );
1159 zone->SetIsIsland( layer, idx );
1162 poly->UpdateTriangulationDataHash();
1163 zone->CalculateFilledArea();
1173 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
1175 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
1178 polys_to_check.reserve(
m_board->GetCopperLayerCount() * aZones.size() );
1180 for(
ZONE* zone : aZones )
1190 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
1197 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
1201 auto island_lambda =
1202 [&](
int aStart,
int aEnd ) -> island_check_return
1204 island_check_return retval;
1206 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
1208 auto [poly, minArea] = polys_to_check[ii];
1210 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
1215 double island_area = test_poly.
Area();
1217 if( island_area < minArea )
1229 if( intersection.
Area() < island_area / 2.0 )
1230 retval.emplace_back( poly, jj );
1237 auto island_returns =
tp.submit_blocks( 0, polys_to_check.size(), island_lambda );
1241 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
1243 std::future<island_check_return>& ret = island_returns[ii];
1247 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
1249 while( status != std::future_status::ready )
1259 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
1267 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
1269 std::future<island_check_return>& ret = island_returns[ii];
1273 for(
auto& action_item : ret.get() )
1274 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
1278 for(
ZONE* zone : aZones )
1279 zone->CalculateFilledArea();
1292 int holeRadius =
via->GetDrillValue() / 2;
1293 int netcode =
via->GetNetCode();
1294 LSET layers =
via->GetLayerSet() & boardCuMask;
1301 bool zoneReachesVia =
false;
1305 if( zone->GetIsRuleArea() )
1308 if( zone->GetNetCode() != netcode )
1311 if( !zone->IsOnLayer( layer ) )
1314 if( !zone->HasFilledPolysForLayer( layer ) )
1317 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1319 if( fill->IsEmpty() )
1324 if( fill->Contains(
center, -1, holeRadius ) )
1326 zoneReachesVia =
true;
1331 if( !zoneReachesVia )
1339 for(
PAD*
pad : footprint->Pads() )
1342 int netcode =
pad->GetNetCode();
1343 LSET layers =
pad->GetLayerSet() & boardCuMask;
1349 if(
pad->HasHole() )
1350 holeRadius = std::min(
pad->GetDrillSizeX(),
pad->GetDrillSizeY() ) / 2;
1357 bool zoneReachesPad =
false;
1361 if( zone->GetIsRuleArea() )
1364 if( zone->GetNetCode() != netcode )
1367 if( !zone->IsOnLayer( layer ) )
1370 if( !zone->HasFilledPolysForLayer( layer ) )
1373 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1375 if( fill->IsEmpty() )
1378 if( fill->Contains(
center, -1, holeRadius ) )
1380 zoneReachesPad =
true;
1385 if( !zoneReachesPad )
1393 bool outOfDate =
false;
1395 for(
ZONE* zone : aZones )
1398 if( zone->GetIsRuleArea() )
1403 zone->BuildHashValue( layer );
1405 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
1411 &&
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
1413 KIDIALOG dlg( aParent,
_(
"Prototype zone fill enabled. Disable setting and refill?" ),
_(
"Confirmation" ),
1414 wxOK | wxCANCEL | wxICON_WARNING );
1420 m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill =
false;
1422 else if( !outOfDate )
1430 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
_(
"Confirmation" ),
1431 wxOK | wxCANCEL | wxICON_WARNING );
1474 std::vector<VECTOR2I> convex_hull;
1479 for(
const VECTOR2I& pt : convex_hull )
1512 minorAxis = std::min( padSize.
x, padSize.
y );
1518 minorAxis =
via->GetWidth( aLayer );
1532 switch( aItem->
Type() )
1539 if(
text->IsVisible() )
1541 if(
text->IsKnockout() )
1597 std::vector<BOARD_ITEM*>& aThermalConnectionPads,
1598 std::vector<PAD*>& aNoConnectionPads )
1604 std::shared_ptr<SHAPE> padShape;
1609 std::unordered_set<PAD_KNOCKOUT_KEY, PAD_KNOCKOUT_KEY_HASH> processedPads;
1610 std::unordered_set<VIA_KNOCKOUT_KEY, VIA_KNOCKOUT_KEY_HASH> processedVias;
1614 for(
PAD*
pad : footprint->Pads() )
1616 if( !
pad->IsOnLayer( aLayer ) )
1619 BOX2I padBBox =
pad->GetBoundingBox();
1636 int drill = std::max(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
1637 int maxDim = std::max( { padSize.
x, padSize.
y, drill } );
1638 effectiveSize =
VECTOR2I( maxDim, maxDim );
1642 effectiveSize = padSize;
1645 PAD_KNOCKOUT_KEY padKey{
pad->GetPosition(), effectiveSize,
1646 static_cast<int>( padShapeType ),
1647 pad->GetOrientation(),
pad->GetNetCode() };
1649 if( !processedPads.insert( padKey ).second )
1653 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1660 noConnection =
true;
1665 if(
pad->IsBackdrilledOrPostMachined( aLayer ) )
1666 noConnection =
true;
1671 aNoConnectionPads.push_back(
pad );
1686 switch( connection )
1692 if( aFill.
Collide( padShape.get(), 0 ) )
1700 aThermalConnectionPads.push_back(
pad );
1709 aNoConnectionPads.push_back(
pad );
1734 switch( connection )
1739 if( aFill.
Collide( padShape.get(), 0 ) )
1744 aThermalConnectionPads.push_back(
pad );
1758 if(
pad->FlashLayer( aLayer ) )
1762 else if(
pad->GetDrillSize().x > 0 )
1769 holeClearance = padClearance;
1793 if( !
via->IsOnLayer( aLayer ) )
1796 BOX2I viaBBox =
via->GetBoundingBox();
1803 int viaEffectiveSize = std::max(
via->GetDrillValue(),
via->GetWidth( aLayer ) );
1804 VIA_KNOCKOUT_KEY viaKey{
via->GetPosition(), viaEffectiveSize,
1805 via->GetNetCode() };
1807 if( !processedVias.insert( viaKey ).second )
1810 bool noConnection =
via->GetNetCode() != aZone->
GetNetCode()
1812 && aLayer !=
via->Padstack().Drill().start
1813 && aLayer !=
via->Padstack().Drill().end );
1816 if(
via->IsBackdrilledOrPostMachined( aLayer ) )
1818 noConnection =
true;
1830 pmSize = std::max( pmSize, frontPM.
size );
1836 pmSize = std::max( pmSize, backPM.
size );
1842 bdSize = secDrill.
size.
x;
1844 int knockoutSize = std::max( pmSize, bdSize );
1846 if( knockoutSize > 0 )
1862 aThermalConnectionPads.push_back(
via );
1877 const std::vector<PAD*>& aNoConnectionPads,
1879 bool aIncludeZoneClearances )
1885 std::unordered_set<PAD_KNOCKOUT_KEY, PAD_KNOCKOUT_KEY_HASH> processedPads;
1886 std::unordered_set<VIA_KNOCKOUT_KEY, VIA_KNOCKOUT_KEY_HASH> processedVias;
1887 std::unordered_set<TRACK_KNOCKOUT_KEY, TRACK_KNOCKOUT_KEY_HASH> processedTracks;
1889 auto checkForCancel =
1892 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1905 auto evalRulesForItems =
1919 auto knockoutPadClearance =
1924 bool hasHole = aPad->GetDrillSize().x > 0;
1925 bool flashLayer = aPad->FlashLayer( aLayer );
1928 if( flashLayer || platedHole )
1933 if( flashLayer && gap >= 0 )
1934 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1951 if( aPad->IsBackdrilledOrPostMachined( aLayer ) )
1962 pmSize = std::max( pmSize, frontPM.
size );
1968 pmSize = std::max( pmSize, backPM.
size );
1974 bdSize = secDrill.
size.
x;
1976 int knockoutSize = std::max( pmSize, bdSize );
1978 if( knockoutSize > 0 )
1980 int clearance = std::max( gap, 0 ) + extra_margin;
1988 for(
PAD*
pad : aNoConnectionPads )
2004 int drill = std::max(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
2005 int maxDim = std::max( { padSize.
x, padSize.
y, drill } );
2006 effectiveSize =
VECTOR2I( maxDim, maxDim );
2010 effectiveSize = padSize;
2013 PAD_KNOCKOUT_KEY padKey{
pad->GetPosition(), effectiveSize,
2014 static_cast<int>( padShape ),
pad->GetOrientation(),
2015 pad->GetNetCode() };
2017 if( !processedPads.insert( padKey ).second )
2021 knockoutPadClearance(
pad );
2026 auto knockoutTrackClearance =
2029 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
2031 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
2053 if(
via->FlashLayer( aLayer ) && gap > 0 )
2055 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
m_maxError,
2074 if(
via->IsBackdrilledOrPostMachined( aLayer ) )
2085 pmSize = std::max( pmSize, frontPM.
size );
2091 pmSize = std::max( pmSize, backPM.
size );
2097 bdSize = secDrill.
size.
x;
2099 int knockoutSize = std::max( pmSize, bdSize );
2101 if( knockoutSize > 0 )
2103 int clearance = std::max( gap, 0 ) + extra_margin;
2114 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
m_maxError,
2123 if( !track->IsOnLayer( aLayer ) )
2133 int viaEffectiveSize = std::max(
via->GetDrillValue(),
via->GetWidth( aLayer ) );
2134 VIA_KNOCKOUT_KEY viaKey{
via->GetPosition(), viaEffectiveSize,
via->GetNetCode() };
2136 if( !processedVias.insert( viaKey ).second )
2141 TRACK_KNOCKOUT_KEY trackKey( track->GetStart(), track->GetEnd(), track->GetWidth() );
2143 if( !processedTracks.insert( trackKey ).second )
2147 knockoutTrackClearance( track );
2152 auto knockoutGraphicClearance =
2158 shapeNet =
static_cast<PCB_SHAPE*
>( aItem )->GetNetCode();
2160 bool sameNet = shapeNet == aZone->
GetNetCode();
2166 if( aItem->IsOnLayer( aLayer )
2168 || aItem->IsOnLayer(
Margin ) )
2170 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2172 bool ignoreLineWidths =
false;
2175 if( aItem->IsOnLayer( aLayer ) && !sameNet )
2179 else if( aItem->IsOnLayer(
Edge_Cuts ) )
2182 ignoreLineWidths =
true;
2184 else if( aItem->IsOnLayer(
Margin ) )
2191 gap += extra_margin;
2192 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
2198 auto knockoutCourtyardClearance =
2201 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
2207 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
2220 knockoutCourtyardClearance( footprint );
2221 knockoutGraphicClearance( &footprint->Reference() );
2222 knockoutGraphicClearance( &footprint->Value() );
2224 std::set<PAD*> allowedNetTiePads;
2228 if( footprint->IsNetTie() )
2230 for(
PAD*
pad : footprint->Pads() )
2239 if(
pad->IsOnLayer( aLayer ) )
2240 allowedNetTiePads.insert(
pad );
2242 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
2244 if( other->IsOnLayer( aLayer ) )
2245 allowedNetTiePads.insert( other );
2251 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2256 BOX2I itemBBox = item->GetBoundingBox();
2258 if( !zone_boundingbox.
Intersects( itemBBox ) )
2261 bool skipItem =
false;
2263 if( item->IsOnLayer( aLayer ) )
2265 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
2267 for(
PAD*
pad : allowedNetTiePads )
2269 if(
pad->GetBoundingBox().Intersects( itemBBox )
2270 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
2279 knockoutGraphicClearance( item );
2288 knockoutGraphicClearance( item );
2293 auto knockoutZoneClearance =
2294 [&](
ZONE* aKnockout )
2297 if( !aKnockout->GetLayerSet().test( aLayer ) )
2300 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
2302 if( aKnockout->GetIsRuleArea() )
2304 if( aKnockout->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2313 if( aKnockout->HigherPriority( aZone ) && !aKnockout->SameNet( aZone ) )
2325 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
m_maxError,
2333 if( aIncludeZoneClearances )
2340 knockoutZoneClearance( otherZone );
2345 for(
ZONE* otherZone : footprint->Zones() )
2350 knockoutZoneClearance( otherZone );
2371 auto evalRulesForItems =
2383 auto knockoutZoneClearance =
2384 [&](
ZONE* aKnockout )
2386 if( !aKnockout->GetLayerSet().test( aLayer ) )
2389 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
2391 if( aKnockout->GetIsRuleArea() )
2393 if( aKnockout->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2401 if( aKnockout->HigherPriority( aZone ) && !aKnockout->SameNet( aZone ) )
2413 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
m_maxError,
2422 knockoutZoneClearance( otherZone );
2426 for(
ZONE* otherZone : footprint->Zones() )
2427 knockoutZoneClearance( otherZone );
2442 auto knockoutZoneOutline =
2443 [&](
ZONE* aKnockout )
2446 if( !aKnockout->GetLayerSet().test( aLayer ) )
2449 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
2465 if( otherZone->SameNet( aZone )
2469 if( !otherZone->IsTeardropArea() )
2470 knockoutZoneOutline( otherZone );
2476 for(
ZONE* otherZone : footprint->Zones() )
2478 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
2481 if( !otherZone->IsTeardropArea() )
2482 knockoutZoneOutline( otherZone );
2500 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
2512 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt1 } );
2513 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt2 } );
2516 for(
auto& [outline, vertices] : insertion_points )
2520 if( vertices.empty() )
2525 std::stable_sort( vertices.begin(), vertices.end(),
2526 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
2528 return a.first < b.first;
2531 std::vector<VECTOR2I> new_points;
2532 new_points.reserve( line.
PointCount() + vertices.size() );
2534 size_t vertex_idx = 0;
2538 new_points.push_back( line.
CPoint( i ) );
2541 while( vertex_idx < vertices.size() && vertices[vertex_idx].first == i )
2543 new_points.push_back( vertices[vertex_idx].second );
2550 for(
const auto& pt : new_points )
2572 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
2574 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
2575 BOX2I islandExtents;
2577 for(
const VECTOR2I& pt : island.front().CPoints() )
2579 islandExtents.
Merge( pt );
2595#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
2596 { if( m_debugZoneFiller && aDebugLayer == b ) \
2598 m_board->SetLayerName( b, c ); \
2599 SHAPE_POLY_SET d = a; \
2639 std::vector<BOARD_ITEM*> thermalConnectionPads;
2640 std::vector<PAD*> noConnectionPads;
2641 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
2644 aFillPolys = aSmoothedOutline;
2671 aFillPolys, thermalRings );
2704 static const bool USE_BBOX_CACHES =
true;
2731 const VECTOR2I& testPt = spoke.CPoint( 3 );
2734 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
2743 if( interval++ > 400 )
2756 if( &other != &spoke
2757 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
2758 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
2794 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
2796 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
2797 BOX2I islandExtents;
2799 for(
const VECTOR2I& pt : island.front().CPoints() )
2801 islandExtents.
Merge( pt );
2822 || !
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
2866 for(
BOARD_ITEM* item : thermalConnectionPads )
2880 bool knockoutsApplied =
false;
2882 if( iterativeRefill )
2892 wxT(
"Cached pre-knockout fill for zone %s layer %d: %d outlines, area %.0f, "
2893 "bbox (%d,%d)-(%d,%d)" ),
2894 aZone->
GetNetname(),
static_cast<int>( aLayer ),
2905 knockoutsApplied =
true;
2913 double preSubtractArea = aFillPolys.
Area();
2916 if( aFillPolys.
Area() < preSubtractArea )
2917 knockoutsApplied =
true;
2921 if( knockoutsApplied )
2939 auto checkForCancel =
2942 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
2945 auto knockoutGraphicItem =
2948 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
2949 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2951 addKnockout( aItem, aLayer, 0,
true, clearanceHoles );
2960 knockoutGraphicItem( &footprint->Reference() );
2961 knockoutGraphicItem( &footprint->Value() );
2963 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2964 knockoutGraphicItem( item );
2972 knockoutGraphicItem( item );
2975 aFillPolys = aSmoothedOutline;
2978 auto subtractKeepout =
2979 [&](
ZONE* candidate )
2981 if( !candidate->GetIsRuleArea() )
2984 if( !candidate->HasKeepoutParametersSet() )
2987 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
2989 if( candidate->GetBoundingBox().Intersects( zone_boundingbox ) )
2991 if( candidate->Outline()->ArcCount() == 0 )
3010 subtractKeepout( keepout );
3018 for(
ZONE* keepout : footprint->Zones() )
3019 subtractKeepout( keepout );
3062 debugLayer = aLayer;
3066 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
3074 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
3091 const std::vector<BOARD_ITEM*>& aSpokedPadsList,
3092 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
3111 if( !item->IsOnLayer( aLayer ) )
3114 int thermalReliefGap = 0;
3118 bool circular =
false;
3122 pad =
static_cast<PAD*
>( item );
3152 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
3153 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
3154 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
3156 if( spoke_w < aZone->GetMinThickness() )
3169 spoke_w = std::min( spoke_w,
via->GetWidth( aLayer ) );
3171 if( spoke_w < aZone->GetMinThickness() )
3190 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
3192 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
3195 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
3198 if( spoke_w < aZone->GetMinThickness() )
3207 int spoke_half_w = spoke_w / 2;
3210 BOX2I itemBB = item->GetBoundingBox();
3216 bool customSpokes =
false;
3220 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
3222 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
3224 customSpokes =
true;
3235 auto buildSpokesFromOrigin =
3242 auto intersectBBox =
3245 double dx = spokeAngle.
Cos();
3246 double dy = spokeAngle.
Sin();
3252 *spoke_side =
VECTOR2I( spoke_half_w, 0 );
3253 return KiROUND( 0.0, dy * half_size.
y );
3257 *spoke_side =
VECTOR2I( 0, spoke_half_w );
3258 return KiROUND( dx * half_size.
x, 0.0 );
3263 double dist_x = half_size.
x /
std::abs( dx );
3264 double dist_y = half_size.
y /
std::abs( dy );
3266 if( dist_x < dist_y )
3268 *spoke_side =
KiROUND( 0.0, spoke_half_w / (
ANGLE_90 - spokeAngle ).Sin() );
3269 return KiROUND( dx * dist_x, dy * dist_x );
3273 *spoke_side =
KiROUND( spoke_half_w / spokeAngle.
Sin(), 0.0 );
3274 return KiROUND( dx * dist_y, dy * dist_y );
3287 for(
const EDA_ANGLE& spokeAngle : angles )
3290 VECTOR2I intersection = intersectBBox( spokeAngle, &spoke_side );
3299 aSpokesList.push_back( std::move( spoke ) );
3311 thermalOutline = thermalPoly.
Outline( 0 );
3315 auto trimToOutline = [&](
SEG& aSegment )
3319 if( padOutline.
Intersect( aSegment, intersections ) )
3321 intersections.clear();
3324 if( thermalOutline.
Intersect( aSegment, intersections ) )
3326 aSegment.B = intersections.front().p;
3333 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
3335 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
3337 SEG seg( primitive->GetStart(), primitive->GetEnd() );
3342 seg.
A +=
pad->ShapePos( aLayer );
3343 seg.
B +=
pad->ShapePos( aLayer );
3357 if( trimToOutline( seg ) )
3359 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
3363 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
3364 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
3367 if( trimToOutline( segL ) && trimToOutline( segR ) )
3375 spoke.
Append( seg.
A + offset );
3376 spoke.
Append( seg.
A - offset );
3378 spoke.
Append( segL.
B + direction );
3379 spoke.
Append( seg.
B + direction );
3380 spoke.
Append( segR.
B + direction );
3383 aSpokesList.push_back( std::move( spoke ) );
3396 thermalSpokeAngle =
pad->GetThermalSpokeAngle();
3415 position =
pad->ShapePos( aLayer );
3416 orientation =
pad->GetOrientation();
3424 position =
via->GetPosition();
3429 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
3436 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
3438 if( thermalSpokeAngle !=
ANGLE_0 )
3441 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
3442 it->Rotate( thermalSpokeAngle );
3447 buildSpokesFromOrigin( spokesBox, thermalSpokeAngle );
3450 auto spokeIter = aSpokesList.rbegin();
3452 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
3454 spokeIter->Rotate( orientation );
3455 spokeIter->Move( position );
3460 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
3461 aSpokesList[ii].GenerateBBoxCache();
3467 const std::vector<BOARD_ITEM*>& aThermalConnectionPads,
3474 for(
BOARD_ITEM* item : aThermalConnectionPads )
3476 if( !item->IsOnLayer( aLayer ) )
3481 bool isCircular =
false;
3489 pad =
static_cast<PAD*
>( item );
3491 position =
pad->ShapePos( aLayer );
3497 padRadius = std::max( padSize.
x, padSize.
y ) / 2;
3506 int spokeMaxWidth = std::min( padSize.
x, padSize.
y );
3507 spokeWidth = std::min( spokeWidth, spokeMaxWidth );
3512 position =
via->GetPosition();
3514 padRadius =
via->GetWidth( aLayer ) / 2;
3523 spokeWidth = std::min( spokeWidth, padRadius * 2 );
3531 if( spokeWidth < aZone->GetMinThickness() )
3541 int ringInnerRadius = padRadius + thermalGap;
3542 int ringWidth = spokeWidth;
3555 pad->TransformShapeToPolygon( outerShape, aLayer, thermalGap + spokeWidth,
3559 pad->TransformShapeToPolygon( innerShape, aLayer, thermalGap,
3562 thermalRing = outerShape;
3594 int maxError =
m_board->GetDesignSettings().m_MaxError;
3610 hole_base.
Append( corner );
3611 corner.
x += hole_size;
3612 hole_base.
Append( corner );
3613 corner.
y += hole_size;
3614 hole_base.
Append( corner );
3616 hole_base.
Append( corner );
3636 #define SMOOTH_MIN_VAL_MM 0.02
3637 #define SMOOTH_SMALL_VAL_MM 0.04
3653 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
3656 maxError = std::max( maxError * 2, smooth_value / 20 );
3658 switch( smooth_level )
3670 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
3682 auto& defaultOffsets =
m_board->GetDesignSettings().m_ZoneLayerProperties;
3685 VECTOR2I offset = defaultOffsets[aLayer].hatching_offset.value_or(
VECTOR2I() );
3687 if( localOffsets.contains( aLayer ) && localOffsets.at( aLayer ).hatching_offset.has_value() )
3688 offset = localOffsets.at( aLayer ).hatching_offset.value();
3690 int x_offset = bbox.
GetX() - ( bbox.
GetX() ) % gridsize - gridsize;
3691 int y_offset = bbox.
GetY() - ( bbox.
GetY() ) % gridsize - gridsize;
3694 for(
int xx = x_offset; xx <= bbox.
GetRight(); xx += gridsize )
3696 for(
int yy = y_offset; yy <= bbox.
GetBottom(); yy += gridsize )
3707 hole.
Move(
VECTOR2I( offset.
x % gridsize, offset.
y % gridsize ) );
3720 deflated_thickness = std::max( deflated_thickness, maxError * 2 );
3742 if( area < minimal_hole_area )
3753 BOX2I thermalBBox = aThermalRings.
BBox();
3756 for(
int holeIdx = holes.
OutlineCount() - 1; holeIdx >= 0; holeIdx-- )
3766 for(
int ringIdx = 0; ringIdx < aThermalRings.
OutlineCount(); ringIdx++ )
3773 if( !holeBBox.
Contains( ringBBox ) )
3789 if( intersections.empty() )
3810 auto cacheKey = std::make_pair(
static_cast<const ZONE*
>( aZone ), aLayer );
3818 wxLogTrace(
traceZoneFiller, wxT(
"Cache miss for zone %s layer %d (cache size: %zu)" ),
3823 wxLogTrace(
traceZoneFiller, wxT(
"Cache hit for zone %s layer %d: pre-knockout %d outlines" ),
3824 aZone->
GetNetname(),
static_cast<int>( aLayer ), it->second.OutlineCount() );
3827 aFillPolys = it->second;
3840 auto evalRulesForItems =
3852 auto knockoutZoneFill =
3853 [&](
ZONE* otherZone )
3855 if( otherZone == aZone )
3858 if( !otherZone->GetLayerSet().test( aLayer ) )
3861 if( otherZone->IsTeardropArea() )
3864 if( !otherZone->HigherPriority( aZone ) )
3867 if( !otherZone->GetBoundingBox().Intersects( zoneBBox ) )
3870 if( !otherZone->HasFilledPolysForLayer( aLayer ) )
3873 std::shared_ptr<SHAPE_POLY_SET> otherFill = otherZone->GetFilledPolysList( aLayer );
3875 if( !otherFill || otherFill->OutlineCount() == 0 )
3878 if( otherZone->SameNet( aZone ) )
3885 aZone, otherZone, aLayer ) );
3888 otherZone, aLayer ) );
3901 knockoutZoneFill( otherZone );
3905 for(
ZONE* otherZone : footprint->Zones() )
3906 knockoutZoneFill( otherZone );
3910 auto subtractKeepout =
3911 [&](
ZONE* candidate )
3913 if( !candidate->GetIsRuleArea() )
3916 if( !candidate->HasKeepoutParametersSet() )
3919 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
3921 if( candidate->GetBoundingBox().Intersects( zoneBBox ) )
3923 if( candidate->Outline()->ArcCount() == 0 )
3938 subtractKeepout( keepout );
3942 for(
ZONE* keepout : footprint->Zones() )
3943 subtractKeepout( keepout );
bool operator==(const wxAuiPaneInfo &aLhs, const wxAuiPaneInfo &aRhs)
constexpr EDA_IU_SCALE pcbIUScale
@ ZLO_FORCE_NO_ZONE_CONNECTION
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
wxString GetNetname() const
Container for design settings for a BOARD object.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
int GetBiggestClearanceValue() const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
virtual void SetIsKnockout(bool aKnockout)
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
int GetCopperLayerCount() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
constexpr int GetSizeMax() const
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr coord_type GetY() const
constexpr size_type GetWidth() const
constexpr Vec Centre() const
constexpr coord_type GetX() const
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr const Vec GetCenter() const
constexpr size_type GetHeight() const
constexpr bool Contains(const Vec &aPoint) const
constexpr coord_type GetRight() const
constexpr bool Intersects(const BOX2< Vec > &aRect) const
constexpr coord_type GetBottom() const
Represent a set of changes (additions, deletions or modifications) of a data model (e....
MINOPTMAX< int > & Value()
const MINOPTMAX< int > & GetValue() const
ZONE_CONNECTION m_ZoneConnection
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
DRC_CONSTRAINT EvalZoneConnection(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
KICAD_T Type() const
Returns the type of object.
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
UNCONNECTED_LAYER_MODE UnconnectedLayerMode() const
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
void SetPosition(const VECTOR2I &aPos) override
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
bool TransformHoleToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Build the corner list of the polygonal drill shape in the board coordinate system.
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Function TransformTextToPolySet Convert the text to a polygonSet describing the actual character stro...
void SetPosition(const VECTOR2I &aPoint) override
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A small class to help profiling.
double msecs(bool aSinceLast=false)
A progress reporter interface for use in multi-threaded environments.
RESULTS(int aOutline1, int aOutline2, int aVertex1, int aVertex2)
bool operator<(const RESULTS &aOther) const
VECTOR2I::extended_type ecoord
static SEG::ecoord Square(int a)
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Find all intersection points between our line chain and the segment aSeg.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Clear()
Remove all points from the line chain.
double Area(bool aAbsolute=true) const
Return the area of this chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
std::vector< INTERSECTION > INTERSECTIONS
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
SHAPE_POLY_SET Chamfer(int aDistance)
Return a chamfered version of the polygon set.
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.
void DeletePolygon(int aIdx)
Delete aIdx-th polygon from the set.
double Area()
Return the area of this poly set.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the 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)
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.
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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 BOX2I BBoxFromCaches() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
VERTEX * getPoint(VERTEX *aPt) const
std::set< RESULTS > GetResults() const
VERTEX_CONNECTOR(const BOX2I &aBBox, const SHAPE_POLY_SET &aPolys, int aDist)
std::set< RESULTS > m_results
std::deque< VERTEX > m_vertices
VERTEX * createList(const SHAPE_LINE_CHAIN &points, VERTEX *aTail=nullptr, void *aUserData=nullptr)
Create a list of vertices from a line chain.
void SetBoundingBox(const BOX2I &aBBox)
VERTEX_SET(int aSimplificationLevel)
uint32_t zOrder(const double aX, const double aY) const
Note that while the inputs are doubles, these are scaled by the size of the bounding box to fit into ...
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
void * GetUserData() const
bool isEar(bool aMatchUserData=false) const
Check whether the given vertex is in the middle of an ear.
bool refillZoneFromCache(ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFillPolys)
Refill a zone from cached pre-knockout fill.
void buildCopperItemClearances(const ZONE *aZone, PCB_LAYER_ID aLayer, const std::vector< PAD * > &aNoConnectionPads, SHAPE_POLY_SET &aHoles, bool aIncludeZoneClearances=true)
Removes clearance from the shape for copper items which share the zone's layer but are not connected ...
void buildHatchZoneThermalRings(const ZONE *aZone, PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aSmoothedOutline, const std::vector< BOARD_ITEM * > &aThermalConnectionPads, SHAPE_POLY_SET &aFillPolys, SHAPE_POLY_SET &aThermalRings)
Build thermal rings for pads in hatch zones.
void connect_nearby_polys(SHAPE_POLY_SET &aPolys, double aDistance)
Create strands of zero-width between elements of SHAPE_POLY_SET that are within aDistance of each oth...
void buildThermalSpokes(const ZONE *box, PCB_LAYER_ID aLayer, const std::vector< BOARD_ITEM * > &aSpokedPadsList, std::deque< SHAPE_LINE_CHAIN > &aSpokes)
Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone.
void buildDifferentNetZoneClearances(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aHoles)
Build clearance knockout holes for higher-priority zones on different nets.
ZONE_FILLER(BOARD *aBoard, COMMIT *aCommit)
void subtractHigherPriorityZones(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawFill)
Removes the outlines of higher-proirity zones with the same net.
void knockoutThermalReliefs(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFill, std::vector< BOARD_ITEM * > &aThermalConnectionPads, std::vector< PAD * > &aNoConnectionPads)
Removes thermal reliefs from the shape for any pads connected to the zone.
void addKnockout(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad or via.
SHAPE_POLY_SET m_boardOutline
std::map< std::pair< const ZONE *, PCB_LAYER_ID >, SHAPE_POLY_SET > m_preKnockoutFillCache
void SetProgressReporter(PROGRESS_REPORTER *aReporter)
PROGRESS_REPORTER * m_progressReporter
bool fillCopperZone(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, const SHAPE_POLY_SET &aSmoothedOutline, const SHAPE_POLY_SET &aMaxExtents, SHAPE_POLY_SET &aFillPolys)
Function fillCopperZone Add non copper areas polygons (pads and tracks with clearance) to a filled co...
void addHoleKnockout(PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad's hole.
bool fillNonCopperZone(const ZONE *candidate, PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aSmoothedOutline, SHAPE_POLY_SET &aFillPolys)
void postKnockoutMinWidthPrune(const ZONE *aZone, SHAPE_POLY_SET &aFillPolys)
Remove minimum-width violations introduced by zone-to-zone knockouts.
bool addHatchFillTypeOnZone(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET &aFillPolys, const SHAPE_POLY_SET &aThermalRings)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
bool fillSingleZone(ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFillPolys)
Build the filled solid areas polygons from zone outlines (stored in m_Poly) The solid areas can be mo...
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 SetNeedRefill(bool aNeedRefill)
std::optional< int > GetLocalClearance() const override
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
void CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
const BOX2I GetBoundingBox() const override
SHAPE_POLY_SET * Outline()
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
int GetMinThickness() const
bool HigherPriority(const ZONE *aOther) const
int GetHatchThickness() const
double GetHatchHoleMinArea() const
bool IsTeardropArea() const
EDA_ANGLE GetHatchOrientation() const
bool BuildSmoothedPoly(SHAPE_POLY_SET &aSmoothedPoly, PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aBoardOutline, SHAPE_POLY_SET *aSmoothedPolyWithApron=nullptr) const
ZONE_FILL_MODE GetFillMode() const
double GetHatchSmoothingValue() const
int GetHatchSmoothingLevel() const
bool IsOnCopperLayer() const override
unsigned GetAssignedPriority() const
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
CORNER_STRATEGY
define how inflate transform build inflated polygon
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ ROUND_ALL_CORNERS
All angles are rounded.
@ EDGE_CLEARANCE_CONSTRAINT
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
@ THERMAL_RELIEF_GAP_CONSTRAINT
@ HOLE_CLEARANCE_CONSTRAINT
@ PHYSICAL_CLEARANCE_CONSTRAINT
static constexpr EDA_ANGLE ANGLE_0
static constexpr EDA_ANGLE ANGLE_90
a few functions useful in geometry calculations.
bool m_ZoneFillIterativeRefill
Enable iterative zone filling to handle isolated islands in higher priority zones.
bool m_DebugZoneFiller
A mode that dumps the various stages of a F_Cu fill into In1_Cu through In9_Cu.
static const wxChar traceZoneFiller[]
Trace mask for zone filler timing information.
static constexpr std::size_t hash_val(const Types &... args)
@ ALWAYS_FLASHED
Always flashed for connectivity.
PCB_LAYER_ID
A quick note on layer IDs:
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
@ PTH
Plated through hole pad.
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
BARCODE class definition.
A storage class for 128-bit hash value.
A struct recording the isolated and single-pad islands within a zone.
! The properties of a padstack drill. Drill position is always the pad position (origin).
VECTOR2I size
Drill diameter (x == y) or slot dimensions (x != y)
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::priority_thread_pool thread_pool
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D
#define SMOOTH_MIN_VAL_MM
int getHatchFillThermalClearance(const ZONE *aZone, BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
#define DUMP_POLYS_TO_COPPER_LAYER(a, b, c)
#define SMOOTH_SMALL_VAL_MM
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
ZONE_CONNECTION
How pads are covered by copper in zone.
@ THERMAL
Use thermal relief for pads.
@ NONE
Pads are not covered.
@ FULL
pads are covered by copper