30#include <unordered_set>
73 RESULTS(
int aOutline1,
int aOutline2,
int aVertex1,
int aVertex2 ) :
120 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
123 auto check_pt = [&](
VERTEX* p )
125 VECTOR2D diff( p->x - aPt->
x, p->y - aPt->
y );
128 if( dist2 > 0 && dist2 < limit2 && dist2 < min_dist && p->isEar(
true ) )
137 while( p && p->
z <= maxZ )
145 while( p && p->
z >= minZ )
160 std::set<VERTEX*> visited;
173 if( ( visited.empty() || !visited.contains( p ) ) && ( q =
getPoint( p ) ) )
177 if( !visited.contains( q ) &&
179 p->
i, q->
i ).second )
183 visited.insert( p->
prev );
185 visited.insert( p->
next );
188 visited.insert( q->
prev );
190 visited.insert( q->
next );
223struct PAD_KNOCKOUT_KEY
230 bool operator==(
const PAD_KNOCKOUT_KEY& other )
const
232 return position == other.position && effectiveSize == other.effectiveSize
233 && shape == other.shape && orientation == other.orientation;
237struct PAD_KNOCKOUT_KEY_HASH
239 size_t operator()(
const PAD_KNOCKOUT_KEY& key )
const
241 return hash_val( key.position.
x, key.position.
y, key.effectiveSize.
x, key.effectiveSize.
y,
242 key.shape, key.orientation.
AsDegrees() );
247struct VIA_KNOCKOUT_KEY
252 bool operator==(
const VIA_KNOCKOUT_KEY& other )
const
254 return position == other.position && effectiveSize == other.effectiveSize;
258struct VIA_KNOCKOUT_KEY_HASH
260 size_t operator()(
const VIA_KNOCKOUT_KEY& key )
const
262 return hash_val( key.position.
x, key.position.
y, key.effectiveSize );
268struct TRACK_KNOCKOUT_KEY
274 TRACK_KNOCKOUT_KEY(
const VECTOR2I& aStart,
const VECTOR2I& aEnd,
int aWidth ) :
278 if( aStart.
x < aEnd.
x || ( aStart.
x == aEnd.
x && aStart.
y <= aEnd.
y ) )
290 bool operator==(
const TRACK_KNOCKOUT_KEY& other )
const
292 return start == other.start && end == other.end && width == other.width;
296struct TRACK_KNOCKOUT_KEY_HASH
298 size_t operator()(
const TRACK_KNOCKOUT_KEY& key )
const
300 return hash_val( key.start.
x, key.start.
y, key.end.
x, key.end.
y, key.width );
329 wxASSERT_MSG(
m_commit, wxT(
"ZONE_FILLER must have a valid commit to call SetProgressReporter" ) );
345 std::lock_guard<KISPINLOCK> lock(
m_board->GetConnectivity()->GetLock() );
347 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
348 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
HASH_128> oldFillHashes;
349 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
351 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
m_board->GetConnectivity();
358 connectivity->ClearRatsnest();
366 :
_(
"Building zone fills..." ) );
379 zone->CacheBoundingBox();
383 for(
PAD*
pad : footprint->Pads() )
387 pad->BuildEffectiveShapes();
392 for(
ZONE* zone : footprint->Zones() )
393 zone->CacheBoundingBox();
396 footprint->BuildCourtyardCaches();
397 footprint->BuildNetTieCache();
402 auto findHighestPriorityZone =
404 const std::function<bool(
const ZONE* )>& testFn ) ->
ZONE*
406 unsigned highestPriority = 0;
407 ZONE* highestPriorityZone =
nullptr;
412 if( zone->GetIsRuleArea() )
415 if( zone->GetAssignedPriority() < highestPriority )
418 if( !zone->IsOnLayer( itemLayer ) )
422 if( zone->GetNumCorners() <= 2 )
425 if( !zone->GetBoundingBox().Intersects( bbox ) )
428 if( !testFn( zone ) )
432 if( zone->GetAssignedPriority() > highestPriority
433 || zone->GetNetCode() == netcode )
435 highestPriority = zone->GetAssignedPriority();
436 highestPriorityZone = zone;
440 return highestPriorityZone;
443 auto isInPourKeepoutArea =
448 if( !zone->GetIsRuleArea() )
451 if( !zone->HasKeepoutParametersSet() )
454 if( !zone->GetDoNotAllowZoneFills() )
457 if( !zone->IsOnLayer( itemLayer ) )
461 if( zone->GetNumCorners() <= 2 )
464 if( !zone->GetBoundingBox().Intersects( bbox ) )
467 if( zone->Outline()->Contains( testPoint ) )
484 via->ClearZoneLayerOverrides();
486 if( !
via->GetRemoveUnconnected() )
491 int holeRadius =
via->GetDrillValue() / 2 + 1;
492 int netcode =
via->GetNetCode();
493 LSET layers =
via->GetLayerSet() & boardCuMask;
497 [&](
const ZONE* aZone ) ->
bool
499 return aZone->Outline()->Contains(
center, -1, holeRadius );
504 if( !
via->ConditionallyFlashed( layer ) )
507 if( isInPourKeepoutArea( bbox, layer,
center ) )
513 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
518 || layer == padstack.
Drill().
end ) )
534 for(
PAD*
pad : footprint->Pads() )
536 pad->ClearZoneLayerOverrides();
538 if( !
pad->GetRemoveUnconnected() )
543 int netcode =
pad->GetNetCode();
544 LSET layers =
pad->GetLayerSet() & boardCuMask;
547 [&](
const ZONE* aZone ) ->
bool
549 return aZone->Outline()->Contains(
center );
554 if( !
pad->ConditionallyFlashed( layer ) )
557 if( isInPourKeepoutArea( bbox, layer,
center ) )
563 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
574 for(
ZONE* zone : aZones )
577 if( zone->GetIsRuleArea() )
581 if( zone->GetNumCorners() <= 2 )
591 zone->BuildHashValue( layer );
592 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
595 toFill.emplace_back( std::make_pair( zone, layer ) );
604 auto check_fill_dependency =
612 if( aOtherZone->GetFillFlag( aLayer ) )
617 if( aOtherZone->GetIsRuleArea() )
621 if( aOtherZone->GetNumCorners() <= 2 )
625 if( !aOtherZone->GetLayerSet().test( aLayer ) )
632 if( aOtherZone->SameNet( aZone ) )
640 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
647 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
650 ZONE* zone = aFillItem.first;
655 for(
ZONE* otherZone : aZones )
657 if( otherZone == zone )
660 if( check_fill_dependency( zone, layer, otherZone ) )
675 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
677 if( !zoneLock.owns_lock() )
694 auto tesselate_lambda =
695 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
701 ZONE* zone = aFillItem.first;
704 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
706 if( !zoneLock.owns_lock() )
718 std::vector<std::pair<std::future<int>,
int>> returns;
719 returns.reserve( toFill.size() );
721 std::atomic<bool> cancelled(
false );
725 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
727 returns.emplace_back( std::make_pair(
tp.submit_task(
730 return fill_lambda( fillItem );
734 while( !cancelled && finished != 2 * toFill.size() )
736 for(
size_t ii = 0; ii < returns.size(); ++ii )
738 auto& ret = returns[ii];
743 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
745 if( status == std::future_status::ready )
747 if( ret.first.get() )
756 if( ret.second == 0 )
758 returns[ii].first =
tp.submit_task(
761 return fill_lambda( toFill[idx] );
764 else if( ret.second == 1 )
766 returns[ii].first =
tp.submit_task(
769 return tesselate_lambda( toFill[idx] );
776 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
790 for(
auto& ret : returns )
792 if( ret.first.valid() )
794 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
796 while( status != std::future_status::ready )
801 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
820 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
821 connectivity->SetProgressReporter(
nullptr );
826 for(
ZONE* zone : aZones )
829 if( zone->GetIsRuleArea() )
832 zone->SetIsFilled(
true );
839 std::set<ZONE*> zonesWithRemovedIslands;
841 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
844 bool allIslands =
true;
846 for(
const auto& [ layer, layerIslands ] : zoneIslands )
848 if( layerIslands.m_IsolatedOutlines.size()
849 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
859 for(
const auto& [ layer, layerIslands ] : zoneIslands )
864 if( layerIslands.m_IsolatedOutlines.empty() )
867 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
871 std::sort( islands.begin(), islands.end(), std::greater<int>() );
873 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
874 long long int minArea = zone->GetMinIslandArea();
877 wxLogTrace(
traceZoneFiller, wxT(
"Zone %s layer %d: %zu islands to process, mode=%d, poly has %d outlines, area %.0f" ),
878 zone->GetNetname(),
static_cast<int>( layer ), islands.size(),
879 static_cast<int>( mode ), poly->OutlineCount(), poly->Area() );
881 for(
int idx : islands )
887 wxLogTrace(
traceZoneFiller, wxT(
"Removing island %d from zone %s (ALWAYS mode)" ),
888 idx, zone->GetNetname() );
889 poly->DeletePolygonAndTriangulationData( idx,
false );
890 zonesWithRemovedIslands.insert( zone );
894 wxLogTrace(
traceZoneFiller, wxT(
"Removing island %d from zone %s (AREA mode, area=%.0f < min=%.0f)" ),
895 idx, zone->GetNetname(), outline.
Area(
true ),
896 static_cast<double>( minArea ) );
897 poly->DeletePolygonAndTriangulationData( idx,
false );
898 zonesWithRemovedIslands.insert( zone );
902 zone->SetIsIsland( layer, idx );
906 poly->UpdateTriangulationDataHash();
907 zone->CalculateFilledArea();
909 BOX2I bbox = poly->BBox();
910 wxLogTrace(
traceZoneFiller, wxT(
"After island removal, zone %s: %d outlines, area %.0f, bbox (%d,%d)-(%d,%d)" ),
911 zone->GetNetname(), poly->OutlineCount(), poly->Area(),
924 if( iterativeRefill && !zonesWithRemovedIslands.empty() )
928 wxLogTrace(
traceZoneFiller, wxT(
"Iterative refill: %zu zones had islands removed, cache size: %zu" ),
934 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zonesToRefill;
936 for(
ZONE* zoneWithIsland : zonesWithRemovedIslands )
938 BOX2I islandZoneBBox = zoneWithIsland->GetBoundingBox();
941 for(
ZONE* zone : aZones )
944 if( zone == zoneWithIsland )
948 if( zone->GetIsRuleArea() )
952 if( !zoneWithIsland->HigherPriority( zone ) )
956 LSET commonLayers = zone->GetLayerSet() & zoneWithIsland->GetLayerSet();
958 if( commonLayers.none() )
962 if( !zone->GetBoundingBox().Intersects( islandZoneBBox ) )
968 auto fillItem = std::make_pair( zone, layer );
970 if( std::find( zonesToRefill.begin(), zonesToRefill.end(), fillItem ) == zonesToRefill.end() )
972 zonesToRefill.push_back( fillItem );
978 if( !zonesToRefill.empty() )
980 wxLogTrace(
traceZoneFiller, wxT(
"Iterative refill: refilling %zu zone/layer pairs" ),
981 zonesToRefill.size() );
992 auto cached_refill_lambda =
993 [&](
const std::pair<ZONE*, PCB_LAYER_ID>& aFillItem ) ->
int
995 ZONE* zone = aFillItem.first;
1003 wxT(
"Cached refill for zone %s: %d outlines, area %.0f" ),
1011 std::vector<std::pair<std::future<int>,
int>> refillReturns;
1012 refillReturns.reserve( zonesToRefill.size() );
1013 size_t refillFinished = 0;
1015 for(
const auto& fillItem : zonesToRefill )
1017 refillReturns.emplace_back( std::make_pair(
tp.submit_task(
1020 return cached_refill_lambda( fillItem );
1024 while( !cancelled && refillFinished != 2 * zonesToRefill.size() )
1026 for(
size_t ii = 0; ii < refillReturns.size(); ++ii )
1028 auto& ret = refillReturns[ii];
1030 if( ret.second > 1 )
1033 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
1035 if( status == std::future_status::ready )
1037 if( ret.first.get() )
1045 if( ret.second == 0 )
1047 refillReturns[ii].first =
tp.submit_task(
1050 return cached_refill_lambda( zonesToRefill[idx] );
1053 else if( ret.second == 1 )
1055 refillReturns[ii].first =
tp.submit_task(
1058 return tesselate_lambda( zonesToRefill[idx] );
1065 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
1077 for(
auto& ret : refillReturns )
1079 if( ret.first.valid() )
1081 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
1083 while( status != std::future_status::ready )
1088 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
1094 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> refillIslandsMap;
1095 std::set<ZONE*> refillZones;
1097 for(
const auto& [zone, layer] : zonesToRefill )
1098 refillZones.insert( zone );
1100 for(
ZONE* zone : refillZones )
1101 refillIslandsMap[zone] = std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>();
1103 connectivity->FillIsolatedIslandsMap( refillIslandsMap );
1106 for(
const auto& [ zone, zoneIslands ] : refillIslandsMap )
1108 bool allIslands =
true;
1110 for(
const auto& [ layer, layerIslands ] : zoneIslands )
1112 if( layerIslands.m_IsolatedOutlines.size()
1113 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
1123 for(
const auto& [ layer, layerIslands ] : zoneIslands )
1128 if( layerIslands.m_IsolatedOutlines.empty() )
1131 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
1132 std::sort( islands.begin(), islands.end(), std::greater<int>() );
1134 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
1135 long long int minArea = zone->GetMinIslandArea();
1138 for(
int idx : islands )
1143 poly->DeletePolygonAndTriangulationData( idx,
false );
1145 poly->DeletePolygonAndTriangulationData( idx,
false );
1147 zone->SetIsIsland( layer, idx );
1150 poly->UpdateTriangulationDataHash();
1151 zone->CalculateFilledArea();
1155 wxLogTrace(
traceZoneFiller, wxT(
"Iterative refill completed in %0.3f ms" ),
1162 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
1164 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
1167 polys_to_check.reserve(
m_board->GetCopperLayerCount() * aZones.size() );
1169 for(
ZONE* zone : aZones )
1179 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
1186 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
1190 auto island_lambda =
1191 [&](
int aStart,
int aEnd ) -> island_check_return
1193 island_check_return retval;
1195 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
1197 auto [poly, minArea] = polys_to_check[ii];
1199 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
1204 double island_area = test_poly.
Area();
1206 if( island_area < minArea )
1218 if( intersection.
Area() < island_area / 2.0 )
1219 retval.emplace_back( poly, jj );
1226 auto island_returns =
tp.submit_blocks( 0, polys_to_check.size(), island_lambda );
1230 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
1232 std::future<island_check_return>& ret = island_returns[ii];
1236 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
1238 while( status != std::future_status::ready )
1248 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
1256 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
1258 std::future<island_check_return>& ret = island_returns[ii];
1262 for(
auto& action_item : ret.get() )
1263 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
1267 for(
ZONE* zone : aZones )
1268 zone->CalculateFilledArea();
1281 int holeRadius =
via->GetDrillValue() / 2;
1282 int netcode =
via->GetNetCode();
1283 LSET layers =
via->GetLayerSet() & boardCuMask;
1290 bool zoneReachesVia =
false;
1294 if( zone->GetIsRuleArea() )
1297 if( zone->GetNetCode() != netcode )
1300 if( !zone->IsOnLayer( layer ) )
1303 if( !zone->HasFilledPolysForLayer( layer ) )
1306 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1308 if( fill->IsEmpty() )
1313 if( fill->Contains(
center, -1, holeRadius ) )
1315 zoneReachesVia =
true;
1320 if( !zoneReachesVia )
1328 for(
PAD*
pad : footprint->Pads() )
1331 int netcode =
pad->GetNetCode();
1332 LSET layers =
pad->GetLayerSet() & boardCuMask;
1338 if(
pad->HasHole() )
1339 holeRadius = std::min(
pad->GetDrillSizeX(),
pad->GetDrillSizeY() ) / 2;
1346 bool zoneReachesPad =
false;
1350 if( zone->GetIsRuleArea() )
1353 if( zone->GetNetCode() != netcode )
1356 if( !zone->IsOnLayer( layer ) )
1359 if( !zone->HasFilledPolysForLayer( layer ) )
1362 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1364 if( fill->IsEmpty() )
1367 if( fill->Contains(
center, -1, holeRadius ) )
1369 zoneReachesPad =
true;
1374 if( !zoneReachesPad )
1382 bool outOfDate =
false;
1384 for(
ZONE* zone : aZones )
1387 if( zone->GetIsRuleArea() )
1392 zone->BuildHashValue( layer );
1394 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
1400 &&
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
1402 KIDIALOG dlg( aParent,
_(
"Prototype zone fill enabled. Disable setting and refill?" ),
_(
"Confirmation" ),
1403 wxOK | wxCANCEL | wxICON_WARNING );
1409 m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill =
false;
1411 else if( !outOfDate )
1419 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
_(
"Confirmation" ),
1420 wxOK | wxCANCEL | wxICON_WARNING );
1463 std::vector<VECTOR2I> convex_hull;
1468 for(
const VECTOR2I& pt : convex_hull )
1501 minorAxis = std::min( padSize.
x, padSize.
y );
1507 minorAxis =
via->GetWidth( aLayer );
1521 switch( aItem->
Type() )
1528 if(
text->IsVisible() )
1530 if(
text->IsKnockout() )
1586 std::vector<BOARD_ITEM*>& aThermalConnectionPads,
1587 std::vector<PAD*>& aNoConnectionPads )
1593 std::shared_ptr<SHAPE> padShape;
1598 std::unordered_set<PAD_KNOCKOUT_KEY, PAD_KNOCKOUT_KEY_HASH> processedPads;
1599 std::unordered_set<VIA_KNOCKOUT_KEY, VIA_KNOCKOUT_KEY_HASH> processedVias;
1603 for(
PAD*
pad : footprint->Pads() )
1605 if( !
pad->IsOnLayer( aLayer ) )
1608 BOX2I padBBox =
pad->GetBoundingBox();
1625 int drill = std::max(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
1626 int maxDim = std::max( { padSize.
x, padSize.
y, drill } );
1627 effectiveSize =
VECTOR2I( maxDim, maxDim );
1631 effectiveSize = padSize;
1634 PAD_KNOCKOUT_KEY padKey{
pad->GetPosition(), effectiveSize,
1635 static_cast<int>( padShapeType ),
pad->GetOrientation() };
1637 if( !processedPads.insert( padKey ).second )
1641 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1648 noConnection =
true;
1653 if(
pad->IsBackdrilledOrPostMachined( aLayer ) )
1654 noConnection =
true;
1659 aNoConnectionPads.push_back(
pad );
1674 switch( connection )
1680 if( aFill.
Collide( padShape.get(), 0 ) )
1688 aThermalConnectionPads.push_back(
pad );
1697 aNoConnectionPads.push_back(
pad );
1722 switch( connection )
1727 if( aFill.
Collide( padShape.get(), 0 ) )
1732 aThermalConnectionPads.push_back(
pad );
1746 if(
pad->FlashLayer( aLayer ) )
1750 else if(
pad->GetDrillSize().x > 0 )
1757 holeClearance = padClearance;
1781 if( !
via->IsOnLayer( aLayer ) )
1784 BOX2I viaBBox =
via->GetBoundingBox();
1791 int viaEffectiveSize = std::max(
via->GetDrillValue(),
via->GetWidth( aLayer ) );
1792 VIA_KNOCKOUT_KEY viaKey{
via->GetPosition(), viaEffectiveSize };
1794 if( !processedVias.insert( viaKey ).second )
1797 bool noConnection =
via->GetNetCode() != aZone->
GetNetCode()
1799 && aLayer !=
via->Padstack().Drill().start
1800 && aLayer !=
via->Padstack().Drill().end );
1803 if(
via->IsBackdrilledOrPostMachined( aLayer ) )
1805 noConnection =
true;
1817 pmSize = std::max( pmSize, frontPM.
size );
1823 pmSize = std::max( pmSize, backPM.
size );
1829 bdSize = secDrill.
size.
x;
1831 int knockoutSize = std::max( pmSize, bdSize );
1833 if( knockoutSize > 0 )
1849 aThermalConnectionPads.push_back(
via );
1864 const std::vector<PAD*>& aNoConnectionPads,
1866 bool aIncludeZoneClearances )
1872 std::unordered_set<PAD_KNOCKOUT_KEY, PAD_KNOCKOUT_KEY_HASH> processedPads;
1873 std::unordered_set<VIA_KNOCKOUT_KEY, VIA_KNOCKOUT_KEY_HASH> processedVias;
1874 std::unordered_set<TRACK_KNOCKOUT_KEY, TRACK_KNOCKOUT_KEY_HASH> processedTracks;
1876 auto checkForCancel =
1879 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1892 auto evalRulesForItems =
1906 auto knockoutPadClearance =
1911 bool hasHole = aPad->GetDrillSize().x > 0;
1912 bool flashLayer = aPad->FlashLayer( aLayer );
1915 if( flashLayer || platedHole )
1920 if( flashLayer && gap >= 0 )
1921 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1938 if( aPad->IsBackdrilledOrPostMachined( aLayer ) )
1949 pmSize = std::max( pmSize, frontPM.
size );
1955 pmSize = std::max( pmSize, backPM.
size );
1961 bdSize = secDrill.
size.
x;
1963 int knockoutSize = std::max( pmSize, bdSize );
1965 if( knockoutSize > 0 )
1967 int clearance = std::max( gap, 0 ) + extra_margin;
1975 for(
PAD*
pad : aNoConnectionPads )
1991 int drill = std::max(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
1992 int maxDim = std::max( { padSize.
x, padSize.
y, drill } );
1993 effectiveSize =
VECTOR2I( maxDim, maxDim );
1997 effectiveSize = padSize;
2000 PAD_KNOCKOUT_KEY padKey{
pad->GetPosition(), effectiveSize,
2001 static_cast<int>( padShape ),
pad->GetOrientation() };
2003 if( !processedPads.insert( padKey ).second )
2007 knockoutPadClearance(
pad );
2012 auto knockoutTrackClearance =
2015 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
2017 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
2039 if(
via->FlashLayer( aLayer ) && gap > 0 )
2041 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
m_maxError,
2060 if(
via->IsBackdrilledOrPostMachined( aLayer ) )
2071 pmSize = std::max( pmSize, frontPM.
size );
2077 pmSize = std::max( pmSize, backPM.
size );
2083 bdSize = secDrill.
size.
x;
2085 int knockoutSize = std::max( pmSize, bdSize );
2087 if( knockoutSize > 0 )
2089 int clearance = std::max( gap, 0 ) + extra_margin;
2100 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
m_maxError,
2109 if( !track->IsOnLayer( aLayer ) )
2119 int viaEffectiveSize = std::max(
via->GetDrillValue(),
via->GetWidth( aLayer ) );
2120 VIA_KNOCKOUT_KEY viaKey{
via->GetPosition(), viaEffectiveSize };
2122 if( !processedVias.insert( viaKey ).second )
2127 TRACK_KNOCKOUT_KEY trackKey( track->GetStart(), track->GetEnd(), track->GetWidth() );
2129 if( !processedTracks.insert( trackKey ).second )
2133 knockoutTrackClearance( track );
2138 auto knockoutGraphicClearance =
2144 shapeNet =
static_cast<PCB_SHAPE*
>( aItem )->GetNetCode();
2146 bool sameNet = shapeNet == aZone->
GetNetCode();
2152 if( aItem->IsOnLayer( aLayer )
2154 || aItem->IsOnLayer(
Margin ) )
2156 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2158 bool ignoreLineWidths =
false;
2161 if( aItem->IsOnLayer( aLayer ) && !sameNet )
2165 else if( aItem->IsOnLayer(
Edge_Cuts ) )
2168 ignoreLineWidths =
true;
2170 else if( aItem->IsOnLayer(
Margin ) )
2177 gap += extra_margin;
2178 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
2184 auto knockoutCourtyardClearance =
2187 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
2193 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
2206 knockoutCourtyardClearance( footprint );
2207 knockoutGraphicClearance( &footprint->Reference() );
2208 knockoutGraphicClearance( &footprint->Value() );
2210 std::set<PAD*> allowedNetTiePads;
2214 if( footprint->IsNetTie() )
2216 for(
PAD*
pad : footprint->Pads() )
2225 if(
pad->IsOnLayer( aLayer ) )
2226 allowedNetTiePads.insert(
pad );
2228 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
2230 if( other->IsOnLayer( aLayer ) )
2231 allowedNetTiePads.insert( other );
2237 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2242 BOX2I itemBBox = item->GetBoundingBox();
2244 if( !zone_boundingbox.
Intersects( itemBBox ) )
2247 bool skipItem =
false;
2249 if( item->IsOnLayer( aLayer ) )
2251 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
2253 for(
PAD*
pad : allowedNetTiePads )
2255 if(
pad->GetBoundingBox().Intersects( itemBBox )
2256 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
2265 knockoutGraphicClearance( item );
2274 knockoutGraphicClearance( item );
2279 auto knockoutZoneClearance =
2280 [&](
ZONE* aKnockout )
2283 if( !aKnockout->GetLayerSet().test( aLayer ) )
2286 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
2288 if( aKnockout->GetIsRuleArea() )
2301 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
m_maxError,
2308 if( aIncludeZoneClearances )
2316 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
2323 if( otherZone->GetIsRuleArea() )
2325 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2326 knockoutZoneClearance( otherZone );
2328 else if( otherZone->HigherPriority( aZone ) )
2330 if( !otherZone->SameNet( aZone ) )
2331 knockoutZoneClearance( otherZone );
2337 for(
ZONE* otherZone : footprint->Zones() )
2343 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
2346 if( otherZone->GetIsRuleArea() )
2348 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2349 knockoutZoneClearance( otherZone );
2351 else if( otherZone->HigherPriority( aZone ) )
2353 if( !otherZone->SameNet( aZone ) )
2354 knockoutZoneClearance( otherZone );
2377 auto evalRulesForItems =
2389 auto knockoutZoneClearance =
2390 [&](
ZONE* aKnockout )
2392 if( !aKnockout->GetLayerSet().test( aLayer ) )
2395 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
2397 if( aKnockout->GetIsRuleArea() )
2399 aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0,
m_maxError,
2405 aZone, aKnockout, aLayer ) );
2408 aKnockout, aLayer ) );
2411 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
2420 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
2426 if( otherZone->GetIsRuleArea() )
2428 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2429 knockoutZoneClearance( otherZone );
2431 else if( otherZone->HigherPriority( aZone ) )
2433 if( !otherZone->SameNet( aZone ) )
2434 knockoutZoneClearance( otherZone );
2440 for(
ZONE* otherZone : footprint->Zones() )
2442 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
2445 if( otherZone->GetIsRuleArea() )
2447 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2448 knockoutZoneClearance( otherZone );
2450 else if( otherZone->HigherPriority( aZone ) )
2452 if( !otherZone->SameNet( aZone ) )
2453 knockoutZoneClearance( otherZone );
2471 auto knockoutZoneOutline =
2472 [&](
ZONE* aKnockout )
2475 if( !aKnockout->GetLayerSet().test( aLayer ) )
2478 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
2494 if( otherZone->SameNet( aZone )
2498 if( !otherZone->IsTeardropArea() )
2499 knockoutZoneOutline( otherZone );
2505 for(
ZONE* otherZone : footprint->Zones() )
2507 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
2510 if( !otherZone->IsTeardropArea() )
2511 knockoutZoneOutline( otherZone );
2529 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
2541 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt1 } );
2542 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt2 } );
2545 for(
auto& [outline, vertices] : insertion_points )
2549 if( vertices.empty() )
2554 std::stable_sort( vertices.begin(), vertices.end(),
2555 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
2557 return a.first < b.first;
2560 std::vector<VECTOR2I> new_points;
2561 new_points.reserve( line.
PointCount() + vertices.size() );
2563 size_t vertex_idx = 0;
2567 new_points.push_back( line.
CPoint( i ) );
2570 while( vertex_idx < vertices.size() && vertices[vertex_idx].first == i )
2572 new_points.push_back( vertices[vertex_idx].second );
2579 for(
const auto& pt : new_points )
2585#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
2586 { if( m_debugZoneFiller && aDebugLayer == b ) \
2588 m_board->SetLayerName( b, c ); \
2589 SHAPE_POLY_SET d = a; \
2629 std::vector<BOARD_ITEM*> thermalConnectionPads;
2630 std::vector<PAD*> noConnectionPads;
2631 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
2634 aFillPolys = aSmoothedOutline;
2661 aFillPolys, thermalRings );
2694 static const bool USE_BBOX_CACHES =
true;
2721 const VECTOR2I& testPt = spoke.CPoint( 3 );
2724 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
2733 if( interval++ > 400 )
2746 if( &other != &spoke
2747 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
2748 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
2784 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
2786 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
2787 BOX2I islandExtents;
2789 for(
const VECTOR2I& pt : island.front().CPoints() )
2791 islandExtents.
Merge( pt );
2812 || !
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
2856 for(
BOARD_ITEM* item : thermalConnectionPads )
2873 if( iterativeRefill )
2883 wxT(
"Cached pre-knockout fill for zone %s layer %d: %d outlines, area %.0f, "
2884 "bbox (%d,%d)-(%d,%d)" ),
2885 aZone->
GetNetname(),
static_cast<int>( aLayer ),
2913 auto checkForCancel =
2916 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
2919 auto knockoutGraphicItem =
2922 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
2923 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2925 addKnockout( aItem, aLayer, 0,
true, clearanceHoles );
2934 knockoutGraphicItem( &footprint->Reference() );
2935 knockoutGraphicItem( &footprint->Value() );
2937 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2938 knockoutGraphicItem( item );
2946 knockoutGraphicItem( item );
2949 aFillPolys = aSmoothedOutline;
2952 auto subtractKeepout =
2953 [&](
ZONE* candidate )
2955 if( !candidate->GetIsRuleArea() )
2958 if( !candidate->HasKeepoutParametersSet() )
2961 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
2963 if( candidate->GetBoundingBox().Intersects( zone_boundingbox ) )
2965 if( candidate->Outline()->ArcCount() == 0 )
2984 subtractKeepout( keepout );
2992 for(
ZONE* keepout : footprint->Zones() )
2993 subtractKeepout( keepout );
3036 debugLayer = aLayer;
3040 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
3048 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
3065 const std::vector<BOARD_ITEM*>& aSpokedPadsList,
3066 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
3085 if( !item->IsOnLayer( aLayer ) )
3088 int thermalReliefGap = 0;
3092 bool circular =
false;
3096 pad =
static_cast<PAD*
>( item );
3126 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
3127 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
3128 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
3130 if( spoke_w < aZone->GetMinThickness() )
3143 spoke_w = std::min( spoke_w,
via->GetWidth( aLayer ) );
3145 if( spoke_w < aZone->GetMinThickness() )
3164 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
3166 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
3169 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
3172 if( spoke_w < aZone->GetMinThickness() )
3181 int spoke_half_w = spoke_w / 2;
3184 BOX2I itemBB = item->GetBoundingBox();
3190 bool customSpokes =
false;
3194 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
3196 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
3198 customSpokes =
true;
3209 auto buildSpokesFromOrigin =
3216 auto intersectBBox =
3219 double dx = spokeAngle.
Cos();
3220 double dy = spokeAngle.
Sin();
3226 *spoke_side =
VECTOR2I( spoke_half_w, 0 );
3227 return KiROUND( 0.0, dy * half_size.
y );
3231 *spoke_side =
VECTOR2I( 0, spoke_half_w );
3232 return KiROUND( dx * half_size.
x, 0.0 );
3237 double dist_x = half_size.
x /
std::abs( dx );
3238 double dist_y = half_size.
y /
std::abs( dy );
3240 if( dist_x < dist_y )
3242 *spoke_side =
KiROUND( 0.0, spoke_half_w / (
ANGLE_90 - spokeAngle ).Sin() );
3243 return KiROUND( dx * dist_x, dy * dist_x );
3247 *spoke_side =
KiROUND( spoke_half_w / spokeAngle.
Sin(), 0.0 );
3248 return KiROUND( dx * dist_y, dy * dist_y );
3261 for(
const EDA_ANGLE& spokeAngle : angles )
3264 VECTOR2I intersection = intersectBBox( spokeAngle, &spoke_side );
3273 aSpokesList.push_back( std::move( spoke ) );
3285 thermalOutline = thermalPoly.
Outline( 0 );
3289 auto trimToOutline = [&](
SEG& aSegment )
3293 if( padOutline.
Intersect( aSegment, intersections ) )
3295 intersections.clear();
3298 if( thermalOutline.
Intersect( aSegment, intersections ) )
3300 aSegment.B = intersections.front().p;
3307 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
3309 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
3311 SEG seg( primitive->GetStart(), primitive->GetEnd() );
3316 seg.
A +=
pad->ShapePos( aLayer );
3317 seg.
B +=
pad->ShapePos( aLayer );
3331 if( trimToOutline( seg ) )
3333 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
3337 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
3338 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
3341 if( trimToOutline( segL ) && trimToOutline( segR ) )
3349 spoke.
Append( seg.
A + offset );
3350 spoke.
Append( seg.
A - offset );
3352 spoke.
Append( segL.
B + direction );
3353 spoke.
Append( seg.
B + direction );
3354 spoke.
Append( segR.
B + direction );
3357 aSpokesList.push_back( std::move( spoke ) );
3370 thermalSpokeAngle =
pad->GetThermalSpokeAngle();
3389 position =
pad->ShapePos( aLayer );
3390 orientation =
pad->GetOrientation();
3398 position =
via->GetPosition();
3403 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
3410 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
3412 if( thermalSpokeAngle !=
ANGLE_0 )
3415 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
3416 it->Rotate( thermalSpokeAngle );
3421 buildSpokesFromOrigin( spokesBox, thermalSpokeAngle );
3424 auto spokeIter = aSpokesList.rbegin();
3426 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
3428 spokeIter->Rotate( orientation );
3429 spokeIter->Move( position );
3434 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
3435 aSpokesList[ii].GenerateBBoxCache();
3441 const std::vector<BOARD_ITEM*>& aThermalConnectionPads,
3448 for(
BOARD_ITEM* item : aThermalConnectionPads )
3450 if( !item->IsOnLayer( aLayer ) )
3455 bool isCircular =
false;
3463 pad =
static_cast<PAD*
>( item );
3465 position =
pad->ShapePos( aLayer );
3471 padRadius = std::max( padSize.
x, padSize.
y ) / 2;
3480 int spokeMaxWidth = std::min( padSize.
x, padSize.
y );
3481 spokeWidth = std::min( spokeWidth, spokeMaxWidth );
3486 position =
via->GetPosition();
3488 padRadius =
via->GetWidth( aLayer ) / 2;
3497 spokeWidth = std::min( spokeWidth, padRadius * 2 );
3505 if( spokeWidth < aZone->GetMinThickness() )
3515 int ringInnerRadius = padRadius + thermalGap;
3516 int ringWidth = spokeWidth;
3529 pad->TransformShapeToPolygon( outerShape, aLayer, thermalGap + spokeWidth,
3533 pad->TransformShapeToPolygon( innerShape, aLayer, thermalGap,
3536 thermalRing = outerShape;
3568 int maxError =
m_board->GetDesignSettings().m_MaxError;
3584 hole_base.
Append( corner );
3585 corner.
x += hole_size;
3586 hole_base.
Append( corner );
3587 corner.
y += hole_size;
3588 hole_base.
Append( corner );
3590 hole_base.
Append( corner );
3610 #define SMOOTH_MIN_VAL_MM 0.02
3611 #define SMOOTH_SMALL_VAL_MM 0.04
3627 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
3630 maxError = std::max( maxError * 2, smooth_value / 20 );
3632 switch( smooth_level )
3644 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
3656 auto& defaultOffsets =
m_board->GetDesignSettings().m_ZoneLayerProperties;
3659 VECTOR2I offset = defaultOffsets[aLayer].hatching_offset.value_or(
VECTOR2I() );
3661 if( localOffsets.contains( aLayer ) && localOffsets.at( aLayer ).hatching_offset.has_value() )
3662 offset = localOffsets.at( aLayer ).hatching_offset.value();
3664 int x_offset = bbox.
GetX() - ( bbox.
GetX() ) % gridsize - gridsize;
3665 int y_offset = bbox.
GetY() - ( bbox.
GetY() ) % gridsize - gridsize;
3668 for(
int xx = x_offset; xx <= bbox.
GetRight(); xx += gridsize )
3670 for(
int yy = y_offset; yy <= bbox.
GetBottom(); yy += gridsize )
3681 hole.
Move(
VECTOR2I( offset.
x % gridsize, offset.
y % gridsize ) );
3694 deflated_thickness = std::max( deflated_thickness, maxError * 2 );
3716 if( area < minimal_hole_area )
3727 BOX2I thermalBBox = aThermalRings.
BBox();
3730 for(
int holeIdx = holes.
OutlineCount() - 1; holeIdx >= 0; holeIdx-- )
3740 for(
int ringIdx = 0; ringIdx < aThermalRings.
OutlineCount(); ringIdx++ )
3747 if( !holeBBox.
Contains( ringBBox ) )
3763 if( intersections.empty() )
3784 auto cacheKey = std::make_pair(
static_cast<const ZONE*
>( aZone ), aLayer );
3792 wxLogTrace(
traceZoneFiller, wxT(
"Cache miss for zone %s layer %d (cache size: %zu)" ),
3797 wxLogTrace(
traceZoneFiller, wxT(
"Cache hit for zone %s layer %d: pre-knockout %d outlines" ),
3798 aZone->
GetNetname(),
static_cast<int>( aLayer ), it->second.OutlineCount() );
3801 aFillPolys = it->second;
3810 int board_clearance =
m_board->GetDesignSettings().m_MinClearance;
3814 if( otherZone == aZone )
3817 if( !otherZone->GetLayerSet().test( aLayer ) )
3820 if( otherZone->IsTeardropArea() )
3823 if( !otherZone->HigherPriority( aZone ) )
3826 if( !otherZone->GetBoundingBox().Intersects( zoneBBox ) )
3829 std::shared_ptr<SHAPE_POLY_SET> otherFill = otherZone->GetFilledPolysList( aLayer );
3831 if( !otherFill || otherFill->OutlineCount() == 0 )
3834 if( otherZone->SameNet( aZone ) )
3842 int clearance = std::max( zone_clearance, otherZone->GetLocalClearance().value() );
3853 for(
ZONE* otherZone : footprint->Zones() )
3855 if( !otherZone->GetLayerSet().test( aLayer ) )
3858 if( otherZone->IsTeardropArea() )
3861 if( !otherZone->HigherPriority( aZone ) )
3864 if( !otherZone->GetBoundingBox().Intersects( zoneBBox ) )
3867 std::shared_ptr<SHAPE_POLY_SET> otherFill = otherZone->GetFilledPolysList( aLayer );
3869 if( !otherFill || otherFill->OutlineCount() == 0 )
3872 if( otherZone->SameNet( aZone ) )
3878 int clearance = std::max( zone_clearance, otherZone->GetLocalClearance().value() );
3889 auto subtractKeepout =
3890 [&](
ZONE* candidate )
3892 if( !candidate->GetIsRuleArea() )
3895 if( !candidate->HasKeepoutParametersSet() )
3898 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
3900 if( candidate->GetBoundingBox().Intersects( zoneBBox ) )
3902 if( candidate->Outline()->ArcCount() == 0 )
3917 subtractKeepout( keepout );
3921 for(
ZONE* keepout : footprint->Zones() )
3922 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 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.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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.
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)
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