30#include <unordered_map>
31#include <unordered_set>
68 RESULTS(
int aOutline1,
int aOutline2,
int aVertex1,
int aVertex2 ) :
115 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
118 auto check_pt = [&](
VERTEX* p )
120 VECTOR2D diff( p->x - aPt->
x, p->y - aPt->
y );
123 if( dist2 > 0 && dist2 < limit2 && dist2 < min_dist && p->isEar(
true ) )
132 while( p && p->
z <= maxZ )
140 while( p && p->
z >= minZ )
155 std::set<VERTEX*> visited;
168 if( ( visited.empty() || !visited.contains( p ) ) && ( q =
getPoint( p ) ) )
172 if( !visited.contains( q ) &&
174 p->
i, q->
i ).second )
178 visited.insert( p->
prev );
180 visited.insert( p->
next );
183 visited.insert( q->
prev );
185 visited.insert( q->
next );
220struct PAD_KNOCKOUT_KEY
228 bool operator==(
const PAD_KNOCKOUT_KEY& other )
const
230 return position == other.position && effectiveSize == other.effectiveSize
231 && shape == other.shape && orientation == other.orientation
232 && netCode == other.netCode;
236struct PAD_KNOCKOUT_KEY_HASH
238 size_t operator()(
const PAD_KNOCKOUT_KEY& key )
const
240 return hash_val( key.position.
x, key.position.
y, key.effectiveSize.
x, key.effectiveSize.
y,
241 key.shape, key.orientation.
AsDegrees(), key.netCode );
248struct VIA_KNOCKOUT_KEY
254 bool operator==(
const VIA_KNOCKOUT_KEY& other )
const
256 return position == other.position && effectiveSize == other.effectiveSize
257 && netCode == other.netCode;
261struct VIA_KNOCKOUT_KEY_HASH
263 size_t operator()(
const VIA_KNOCKOUT_KEY& key )
const
265 return hash_val( key.position.
x, key.position.
y, key.effectiveSize, key.netCode );
271struct TRACK_KNOCKOUT_KEY
277 TRACK_KNOCKOUT_KEY(
const VECTOR2I& aStart,
const VECTOR2I& aEnd,
int aWidth ) :
281 if( aStart.
x < aEnd.
x || ( aStart.
x == aEnd.
x && aStart.
y <= aEnd.
y ) )
293 bool operator==(
const TRACK_KNOCKOUT_KEY& other )
const
295 return start == other.start && end == other.end && width == other.width;
299struct TRACK_KNOCKOUT_KEY_HASH
301 size_t operator()(
const TRACK_KNOCKOUT_KEY& key )
const
303 return hash_val( key.start.
x, key.start.
y, key.end.
x, key.end.
y, key.width );
307template<
typename Func>
308void forEachBoardAndFootprintZone(
BOARD* aBoard, Func&& aFunc )
315 for(
ZONE* zone : footprint->Zones() )
382 std::lock_guard<KISPINLOCK> lock(
m_board->GetConnectivity()->GetLock() );
384 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
385 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
HASH_128> oldFillHashes;
386 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
388 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
m_board->GetConnectivity();
395 connectivity->ClearRatsnest();
403 :
_(
"Building zone fills..." ) );
416 zone->CacheBoundingBox();
420 for(
PAD*
pad : footprint->Pads() )
424 pad->BuildEffectiveShapes();
429 for(
ZONE* zone : footprint->Zones() )
430 zone->CacheBoundingBox();
433 footprint->BuildCourtyardCaches();
434 footprint->BuildNetTieCache();
441 std::unordered_map<const ZONE*, POLY_YSTRIPES_INDEX> zoneOutlineIndices;
445 if( zone->GetNumCorners() <= 2 )
448 zoneOutlineIndices[zone].Build( *zone->Outline() );
451 auto findHighestPriorityZone =
453 const std::function<bool(
const ZONE* )>& testFn ) ->
ZONE*
455 unsigned highestPriority = 0;
456 ZONE* highestPriorityZone =
nullptr;
461 if( zone->GetIsRuleArea() )
464 if( zone->GetAssignedPriority() < highestPriority )
467 if( !zone->IsOnLayer( itemLayer ) )
471 if( zone->GetNumCorners() <= 2 )
474 if( !zone->GetBoundingBox().Intersects( bbox ) )
477 if( !testFn( zone ) )
481 if( zone->GetAssignedPriority() > highestPriority
482 || zone->GetNetCode() == netcode )
484 highestPriority = zone->GetAssignedPriority();
485 highestPriorityZone = zone;
489 return highestPriorityZone;
492 auto isInPourKeepoutArea =
497 if( !zone->GetIsRuleArea() )
500 if( !zone->HasKeepoutParametersSet() )
503 if( !zone->GetDoNotAllowZoneFills() )
506 if( !zone->IsOnLayer( itemLayer ) )
510 if( zone->GetNumCorners() <= 2 )
513 if( !zone->GetBoundingBox().Intersects( bbox ) )
516 auto it = zoneOutlineIndices.find( zone );
518 if( it != zoneOutlineIndices.end() && it->second.Contains( testPoint ) )
535 via->ClearZoneLayerOverrides();
537 if( !
via->GetRemoveUnconnected() )
542 int holeRadius =
via->GetDrillValue() / 2 + 1;
543 int netcode =
via->GetNetCode();
544 LSET layers =
via->GetLayerSet() & boardCuMask;
548 [&](
const ZONE* aZone ) ->
bool
555 if( !
via->ConditionallyFlashed( layer ) )
558 if( isInPourKeepoutArea( bbox, layer,
center ) )
564 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
569 || layer == padstack.
Drill().
end ) )
585 for(
PAD*
pad : footprint->Pads() )
587 pad->ClearZoneLayerOverrides();
589 if( !
pad->GetRemoveUnconnected() )
594 int netcode =
pad->GetNetCode();
595 LSET layers =
pad->GetLayerSet() & boardCuMask;
598 [&](
const ZONE* aZone ) ->
bool
600 auto it = zoneOutlineIndices.find( aZone );
602 if( it != zoneOutlineIndices.end() )
603 return it->second.Contains(
center );
610 if( !
pad->ConditionallyFlashed( layer ) )
613 if( isInPourKeepoutArea( bbox, layer,
center ) )
619 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
630 for(
ZONE* zone : aZones )
633 if( zone->GetIsRuleArea() )
637 if( zone->GetNumCorners() <= 2 )
647 zone->BuildHashValue( layer );
648 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
651 toFill.emplace_back( std::make_pair( zone, layer ) );
660 auto zone_fill_dependency =
662 bool aRequireCompletedOtherFill ) ->
bool
669 if( aRequireCompletedOtherFill && aOtherZone->GetFillFlag( aLayer ) )
674 if( aOtherZone->GetIsRuleArea() )
678 if( aOtherZone->GetNumCorners() <= 2 )
682 if( !aOtherZone->GetLayerSet().test( aLayer ) )
689 if( aOtherZone->SameNet( aZone ) )
697 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
703 auto check_fill_dependency =
706 return zone_fill_dependency( aZone, aLayer, aOtherZone,
true );
709 auto fill_item_dependency =
710 [&](
const std::pair<ZONE*, PCB_LAYER_ID>& aWaiter,
711 const std::pair<ZONE*, PCB_LAYER_ID>& aDependency ) ->
bool
713 if( aWaiter.first == aDependency.first || aWaiter.second != aDependency.second )
716 return check_fill_dependency( aWaiter.first, aWaiter.second, aDependency.first );
720 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
726 ZONE* zone = aFillItem.first;
741 auto tesselate_lambda =
742 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
748 ZONE* zone = aFillItem.first;
757 std::atomic<bool> cancelled =
false;
759 auto waitForFutures =
760 [&]( std::vector<std::future<int>>& aFutures, std::vector<int>* aResults = nullptr )
765 for(
auto& future : aFutures )
767 while( future.wait_for( std::chrono::milliseconds( 100 ) )
768 != std::future_status::ready )
779 int result = future.get();
782 aResults->push_back(
result );
788 std::vector<std::vector<size_t>> successors;
789 std::vector<int> inDegree;
790 std::vector<size_t> currentWave;
793 auto build_fill_dag =
794 [&](
const std::vector<std::pair<ZONE*, PCB_LAYER_ID>>& aFillItems,
795 auto&& aHasDependency ) -> FILL_DAG
799 dag.successors.resize( aFillItems.size() );
800 dag.inDegree.assign( aFillItems.size(), 0 );
801 dag.currentWave.reserve( aFillItems.size() );
803 for(
size_t i = 0; i < aFillItems.size(); ++i )
805 for(
size_t j = 0; j < aFillItems.size(); ++j )
810 if( aHasDependency( aFillItems[j], aFillItems[i] ) )
812 dag.successors[i].push_back( j );
818 for(
size_t i = 0; i < aFillItems.size(); ++i )
820 if( dag.inDegree[i] == 0 )
821 dag.currentWave.push_back( i );
827 auto run_fill_waves =
828 [&](
const std::vector<std::pair<ZONE*, PCB_LAYER_ID>>& aFillItems,
auto&& aFillFn,
829 auto&& aTessFn,
auto&& aHasDependency )
831 FILL_DAG dag = build_fill_dag( aFillItems, aHasDependency );
833 while( !dag.currentWave.empty() && !cancelled.load() )
835 std::vector<std::future<int>> fillFutures;
836 std::vector<int> fillResults;
838 fillFutures.reserve( dag.currentWave.size() );
840 for(
size_t idx : dag.currentWave )
842 fillFutures.emplace_back(
tp.submit_task(
843 [&aFillFn, &aFillItems, idx]()
845 return aFillFn( aFillItems[idx] );
849 waitForFutures( fillFutures, &fillResults );
851 std::vector<std::future<int>> tessFutures;
853 tessFutures.reserve( dag.currentWave.size() );
855 for(
size_t ii = 0; ii < fillResults.size(); ++ii )
857 if( fillResults[ii] == 0 )
860 size_t idx = dag.currentWave[ii];
862 tessFutures.emplace_back(
tp.submit_task(
863 [&aTessFn, &aFillItems, idx]()
865 return aTessFn( aFillItems[idx] );
869 waitForFutures( tessFutures );
871 if( cancelled.load() )
874 std::vector<size_t> nextWave;
876 for(
size_t idx : dag.currentWave )
878 for(
size_t succ : dag.successors[idx] )
880 if( --dag.inDegree[succ] == 0 )
881 nextWave.push_back( succ );
885 dag.currentWave = std::move( nextWave );
889 run_fill_waves( toFill, fill_lambda, tesselate_lambda, fill_item_dependency );
904 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
905 connectivity->SetProgressReporter(
nullptr );
910 for(
ZONE* zone : aZones )
913 if( zone->GetIsRuleArea() )
916 zone->SetIsFilled(
true );
923 std::set<ZONE*> zonesWithRemovedIslands;
925 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
928 bool allIslands =
true;
930 for(
const auto& [ layer, layerIslands ] : zoneIslands )
932 if( layerIslands.m_IsolatedOutlines.size()
933 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
943 for(
const auto& [ layer, layerIslands ] : zoneIslands )
948 if( layerIslands.m_IsolatedOutlines.empty() )
951 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
955 std::sort( islands.begin(), islands.end(), std::greater<int>() );
957 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
958 long long int minArea = zone->GetMinIslandArea();
961 for(
int idx : islands )
967 poly->DeletePolygonAndTriangulationData( idx,
false );
968 zonesWithRemovedIslands.insert( zone );
972 poly->DeletePolygonAndTriangulationData( idx,
false );
973 zonesWithRemovedIslands.insert( zone );
977 zone->SetIsIsland( layer, idx );
981 poly->UpdateTriangulationDataHash();
982 zone->CalculateFilledArea();
993 if( iterativeRefill && !zonesWithRemovedIslands.empty() )
998 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> zonesToRefill;
1000 for(
ZONE* zoneWithIsland : zonesWithRemovedIslands )
1002 BOX2I islandZoneBBox = zoneWithIsland->GetBoundingBox();
1005 for(
ZONE* zone : aZones )
1008 if( zone == zoneWithIsland )
1012 if( zone->GetIsRuleArea() )
1016 if( !zoneWithIsland->HigherPriority( zone ) )
1020 LSET commonLayers = zone->GetLayerSet() & zoneWithIsland->GetLayerSet();
1022 if( commonLayers.none() )
1026 if( !zone->GetBoundingBox().Intersects( islandZoneBBox ) )
1032 auto fillItem = std::make_pair( zone, layer );
1034 if( std::find( zonesToRefill.begin(), zonesToRefill.end(), fillItem ) == zonesToRefill.end() )
1036 zonesToRefill.push_back( fillItem );
1042 if( !zonesToRefill.empty() )
1053 auto cached_refill_fill_lambda =
1054 [&](
const std::pair<ZONE*, PCB_LAYER_ID>& aFillItem ) ->
int
1056 ZONE* zone = aFillItem.first;
1068 auto cached_refill_tessellate_lambda =
1069 [&](
const std::pair<ZONE*, PCB_LAYER_ID>& aFillItem ) ->
int
1071 ZONE* zone = aFillItem.first;
1079 auto refill_item_dependency =
1080 [&](
const std::pair<ZONE*, PCB_LAYER_ID>& aWaiter,
1081 const std::pair<ZONE*, PCB_LAYER_ID>& aDependency ) ->
bool
1083 if( aWaiter.first == aDependency.first || aWaiter.second != aDependency.second )
1086 return zone_fill_dependency( aWaiter.first, aWaiter.second,
1087 aDependency.first,
false );
1090 run_fill_waves( zonesToRefill, cached_refill_fill_lambda,
1091 cached_refill_tessellate_lambda, refill_item_dependency );
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 )
1102 refillIslandsMap[zone] = std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>();
1108 connectivity->FillIsolatedIslandsMap( refillIslandsMap );
1111 for(
const auto& [ zone, zoneIslands ] : refillIslandsMap )
1113 bool allIslands =
true;
1115 for(
const auto& [ layer, layerIslands ] : zoneIslands )
1117 if( layerIslands.m_IsolatedOutlines.size()
1118 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
1128 for(
const auto& [ layer, layerIslands ] : zoneIslands )
1133 if( layerIslands.m_IsolatedOutlines.empty() )
1136 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
1137 std::sort( islands.begin(), islands.end(), std::greater<int>() );
1139 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
1140 long long int minArea = zone->GetMinIslandArea();
1143 for(
int idx : islands )
1148 poly->DeletePolygonAndTriangulationData( idx,
false );
1150 poly->DeletePolygonAndTriangulationData( idx,
false );
1152 zone->SetIsIsland( layer, idx );
1155 poly->UpdateTriangulationDataHash();
1156 zone->CalculateFilledArea();
1165 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
1167 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
1170 polys_to_check.reserve(
m_board->GetCopperLayerCount() * aZones.size() );
1172 for(
ZONE* zone : aZones )
1182 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
1189 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
1193 auto island_lambda =
1194 [&](
int aStart,
int aEnd ) -> island_check_return
1196 island_check_return retval;
1198 for(
int ii = aStart; ii < aEnd && !cancelled.load(); ++ii )
1200 auto [poly, minArea] = polys_to_check[ii];
1202 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
1207 double island_area = test_poly.
Area();
1209 if( island_area < minArea )
1221 if( intersection.
Area() < island_area / 2.0 )
1222 retval.emplace_back( poly, jj );
1229 auto island_returns =
tp.submit_blocks( 0, polys_to_check.size(), island_lambda );
1233 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
1235 std::future<island_check_return>& ret = island_returns[ii];
1239 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
1241 while( status != std::future_status::ready )
1251 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
1256 if( cancelled.load() )
1259 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
1261 std::future<island_check_return>& ret = island_returns[ii];
1265 for(
auto& action_item : ret.get() )
1266 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
1270 for(
ZONE* zone : aZones )
1271 zone->CalculateFilledArea();
1284 std::unique_ptr<POLY_YSTRIPES_INDEX>
index;
1287 struct NET_LAYER_HASH
1289 size_t operator()(
const std::pair<int, PCB_LAYER_ID>& k )
const
1291 return std::hash<int>()( k.first ) ^ ( std::hash<int>()( k.second ) << 16 );
1295 std::unordered_map<std::pair<int, PCB_LAYER_ID>, std::vector<INDEXED_ZONE>, NET_LAYER_HASH>
1296 filledZonesByNetLayer;
1300 if( zone->GetIsRuleArea() )
1305 if( !zone->HasFilledPolysForLayer( layer ) )
1308 const std::shared_ptr<SHAPE_POLY_SET>& fill = zone->GetFilledPolysList( layer );
1310 if( fill->IsEmpty() )
1314 iz.bbox = fill->BBox();
1315 iz.index = std::make_unique<POLY_YSTRIPES_INDEX>();
1316 iz.index->Build( *fill );
1317 filledZonesByNetLayer[{ zone->GetNetCode(), layer }].push_back( std::move( iz ) );
1321 auto zoneReachesPoint =
1324 auto it = filledZonesByNetLayer.find( { aNetcode, aLayer } );
1326 if( it == filledZonesByNetLayer.end() )
1329 for(
const INDEXED_ZONE& iz : it->second )
1331 if( !iz.bbox.GetInflated( aRadius ).Contains( aCenter ) )
1334 if( iz.index->Contains( aCenter, aRadius ) )
1348 int holeRadius =
via->GetDrillValue() / 2;
1349 int netcode =
via->GetNetCode();
1350 LSET layers =
via->GetLayerSet() & boardCuMask;
1357 if( !zoneReachesPoint( netcode, layer,
center, holeRadius ) )
1364 for(
PAD*
pad : footprint->Pads() )
1367 int netcode =
pad->GetNetCode();
1368 LSET layers =
pad->GetLayerSet() & boardCuMask;
1372 if(
pad->HasHole() )
1373 holeRadius = std::min(
pad->GetDrillSizeX(),
pad->GetDrillSizeY() ) / 2;
1380 if( !zoneReachesPoint( netcode, layer,
center, holeRadius ) )
1388 bool outOfDate =
false;
1390 for(
ZONE* zone : aZones )
1393 if( zone->GetIsRuleArea() )
1398 zone->BuildHashValue( layer );
1400 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
1406 &&
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
1408 KIDIALOG dlg( aParent,
_(
"Prototype zone fill enabled. Disable setting and refill?" ),
_(
"Confirmation" ),
1409 wxOK | wxCANCEL | wxICON_WARNING );
1415 m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill =
false;
1417 else if( !outOfDate )
1425 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
_(
"Confirmation" ),
1426 wxOK | wxCANCEL | wxICON_WARNING );
1469 std::vector<VECTOR2I> convex_hull;
1474 for(
const VECTOR2I& pt : convex_hull )
1506 switch( aItem->
Type() )
1513 if(
text->IsVisible() )
1515 if(
text->IsKnockout() )
1571 std::vector<BOARD_ITEM*>& aThermalConnectionPads,
1572 std::vector<PAD*>& aNoConnectionPads )
1578 std::shared_ptr<SHAPE> padShape;
1583 std::unordered_set<PAD_KNOCKOUT_KEY, PAD_KNOCKOUT_KEY_HASH> processedPads;
1584 std::unordered_set<VIA_KNOCKOUT_KEY, VIA_KNOCKOUT_KEY_HASH> processedVias;
1588 for(
PAD*
pad : footprint->Pads() )
1594 &&
pad->GetDrillSize().x > 0;
1596 if( !
pad->IsOnLayer( aLayer ) && !npthWithHole )
1599 BOX2I padBBox =
pad->GetBoundingBox();
1616 int drill = std::max(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
1617 int maxDim = std::max( { padSize.
x, padSize.
y, drill } );
1618 effectiveSize =
VECTOR2I( maxDim, maxDim );
1622 effectiveSize = padSize;
1625 PAD_KNOCKOUT_KEY padKey{
pad->GetPosition(), effectiveSize,
1626 static_cast<int>( padShapeType ),
1627 pad->GetOrientation(),
pad->GetNetCode() };
1629 if( !processedPads.insert( padKey ).second )
1633 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1640 noConnection =
true;
1645 if(
pad->IsBackdrilledOrPostMachined( aLayer ) )
1646 noConnection =
true;
1651 aNoConnectionPads.push_back(
pad );
1666 switch( connection )
1672 if( aFill.
Collide( padShape.get(), 0 ) )
1680 aThermalConnectionPads.push_back(
pad );
1689 aNoConnectionPads.push_back(
pad );
1714 switch( connection )
1719 if( aFill.
Collide( padShape.get(), 0 ) )
1724 aThermalConnectionPads.push_back(
pad );
1738 if(
pad->FlashLayer( aLayer ) )
1742 else if(
pad->GetDrillSize().x > 0 )
1749 holeClearance = padClearance;
1775 if( !
via->IsOnLayer( aLayer ) )
1778 BOX2I viaBBox =
via->GetBoundingBox();
1785 int viaEffectiveSize = std::max(
via->GetDrillValue(),
via->GetWidth( aLayer ) );
1786 VIA_KNOCKOUT_KEY viaKey{
via->GetPosition(), viaEffectiveSize,
via->GetNetCode() };
1788 if( !processedVias.insert( viaKey ).second )
1791 bool noConnection =
via->GetNetCode() != aZone->
GetNetCode()
1793 && aLayer !=
via->Padstack().Drill().start
1794 && aLayer !=
via->Padstack().Drill().end );
1797 noConnection =
true;
1800 if(
via->IsBackdrilledOrPostMachined( aLayer ) )
1802 noConnection =
true;
1814 pmSize = std::max( pmSize, frontPM.
size );
1820 pmSize = std::max( pmSize, backPM.
size );
1826 bdSize = secDrill.
size.
x;
1828 int knockoutSize = std::max( pmSize, bdSize );
1830 if( knockoutSize > 0 )
1845 switch( connection )
1855 if( thermalGap > 0 )
1857 aThermalConnectionPads.push_back(
via );
1885 const std::vector<PAD*>& aNoConnectionPads,
1887 bool aIncludeZoneClearances )
1893 std::unordered_set<PAD_KNOCKOUT_KEY, PAD_KNOCKOUT_KEY_HASH> processedPads;
1894 std::unordered_set<VIA_KNOCKOUT_KEY, VIA_KNOCKOUT_KEY_HASH> processedVias;
1895 std::unordered_set<TRACK_KNOCKOUT_KEY, TRACK_KNOCKOUT_KEY_HASH> processedTracks;
1897 auto checkForCancel =
1900 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1913 auto evalRulesForItems =
1927 auto knockoutPadClearance =
1932 bool hasHole = aPad->GetDrillSize().x > 0;
1933 bool flashLayer = aPad->FlashLayer( aLayer );
1936 if( flashLayer || platedHole )
1941 if( flashLayer && gap >= 0 )
1942 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1957 && aPad->GetDrillSize().x != aPad->GetDrillSize().y )
1968 if( aPad->IsBackdrilledOrPostMachined( aLayer ) )
1979 pmSize = std::max( pmSize, frontPM.
size );
1985 pmSize = std::max( pmSize, backPM.
size );
1991 bdSize = secDrill.
size.
x;
1993 int knockoutSize = std::max( pmSize, bdSize );
1995 if( knockoutSize > 0 )
1997 int clearance = std::max( gap, 0 ) + extra_margin;
2005 for(
PAD*
pad : aNoConnectionPads )
2021 int drill = std::max(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
2022 int maxDim = std::max( { padSize.
x, padSize.
y, drill } );
2023 effectiveSize =
VECTOR2I( maxDim, maxDim );
2027 effectiveSize = padSize;
2030 PAD_KNOCKOUT_KEY padKey{
pad->GetPosition(), effectiveSize,
2031 static_cast<int>( padShape ),
pad->GetOrientation(),
2032 pad->GetNetCode() };
2034 if( !processedPads.insert( padKey ).second )
2038 knockoutPadClearance(
pad );
2043 auto knockoutTrackClearance =
2046 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
2048 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
2070 if(
via->FlashLayer( aLayer ) && gap > 0 )
2072 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
m_maxError,
2091 if(
via->IsBackdrilledOrPostMachined( aLayer ) )
2102 pmSize = std::max( pmSize, frontPM.
size );
2108 pmSize = std::max( pmSize, backPM.
size );
2114 bdSize = secDrill.
size.
x;
2116 int knockoutSize = std::max( pmSize, bdSize );
2118 if( knockoutSize > 0 )
2120 int clearance = std::max( gap, 0 ) + extra_margin;
2131 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
m_maxError,
2140 if( !track->IsOnLayer( aLayer ) )
2150 int viaEffectiveSize = std::max(
via->GetDrillValue(),
via->GetWidth( aLayer ) );
2151 VIA_KNOCKOUT_KEY viaKey{
via->GetPosition(), viaEffectiveSize,
via->GetNetCode() };
2153 if( !processedVias.insert( viaKey ).second )
2158 TRACK_KNOCKOUT_KEY trackKey( track->GetStart(), track->GetEnd(), track->GetWidth() );
2160 if( !processedTracks.insert( trackKey ).second )
2164 knockoutTrackClearance( track );
2169 auto knockoutGraphicClearance =
2175 shapeNet =
static_cast<PCB_SHAPE*
>( aItem )->GetNetCode();
2177 bool sameNet = shapeNet == aZone->
GetNetCode();
2183 if( aItem->IsOnLayer( aLayer )
2185 || aItem->IsOnLayer(
Margin ) )
2187 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2189 bool ignoreLineWidths =
false;
2192 if( aItem->IsOnLayer( aLayer ) && !sameNet )
2196 else if( aItem->IsOnLayer(
Edge_Cuts ) )
2199 ignoreLineWidths =
true;
2201 else if( aItem->IsOnLayer(
Margin ) )
2208 gap += extra_margin;
2209 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
2215 auto knockoutCourtyardClearance =
2218 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
2224 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
2237 knockoutCourtyardClearance( footprint );
2238 knockoutGraphicClearance( &footprint->Reference() );
2239 knockoutGraphicClearance( &footprint->Value() );
2241 std::set<PAD*> allowedNetTiePads;
2245 if( footprint->IsNetTie() )
2247 for(
PAD*
pad : footprint->Pads() )
2256 if(
pad->IsOnLayer( aLayer ) )
2257 allowedNetTiePads.insert(
pad );
2259 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
2261 if( other->IsOnLayer( aLayer ) )
2262 allowedNetTiePads.insert( other );
2268 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2273 BOX2I itemBBox = item->GetBoundingBox();
2275 if( !zone_boundingbox.
Intersects( itemBBox ) )
2278 bool skipItem =
false;
2280 if( item->IsOnLayer( aLayer ) )
2282 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
2284 for(
PAD*
pad : allowedNetTiePads )
2286 if(
pad->GetBoundingBox().Intersects( itemBBox )
2287 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
2296 knockoutGraphicClearance( item );
2305 knockoutGraphicClearance( item );
2310 auto knockoutZoneClearance =
2311 [&](
ZONE* aKnockout )
2314 if( !aKnockout->GetLayerSet().test( aLayer ) )
2317 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
2319 if( aKnockout->GetIsRuleArea() )
2321 if( aKnockout->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
2330 if( aKnockout->HigherPriority( aZone ) && !aKnockout->SameNet( aZone ) )
2342 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
m_maxError,
2350 if( aIncludeZoneClearances )
2357 knockoutZoneClearance( otherZone );
2362 for(
ZONE* otherZone : footprint->Zones() )
2367 knockoutZoneClearance( otherZone );
2388 auto evalRulesForItems =
2404 auto knockoutZoneClearance =
2405 [&](
ZONE* aKnockout )
2407 if( aKnockout->GetIsRuleArea() )
2410 if( !aKnockout->GetLayerSet().test( aLayer ) )
2413 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
2415 if( aKnockout->HigherPriority( aZone ) && !aKnockout->SameNet( aZone ) )
2418 aZone, aKnockout, aLayer ) );
2421 aKnockout, aLayer ) );
2427 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
2434 forEachBoardAndFootprintZone(
m_board, knockoutZoneClearance );
2450 auto collectZoneOutline =
2451 [&](
ZONE* aKnockout )
2453 if( !aKnockout->GetLayerSet().test( aLayer ) )
2456 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
2457 appendZoneOutlineWithoutArcs( aKnockout, knockouts );
2460 forEachBoardAndFootprintZone(
2462 [&](
ZONE* otherZone )
2466 bool higherPrioritySameNet =
2471 collectZoneOutline( otherZone );
2490 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
2502 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt1 } );
2503 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt2 } );
2506 for(
auto& [outline, vertices] : insertion_points )
2514 std::stable_sort( vertices.begin(), vertices.end(),
2515 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
2517 return a.first > b.first;
2520 for(
const auto& [vertex, pt] : vertices )
2521 line.
Insert( vertex + 1, pt );
2542 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
2544 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
2545 BOX2I islandExtents;
2547 for(
const VECTOR2I& pt : island.front().CPoints() )
2549 islandExtents.
Merge( pt );
2565#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
2566 { if( m_debugZoneFiller && aDebugLayer == b ) \
2568 m_board->SetLayerName( b, c ); \
2569 SHAPE_POLY_SET d = a; \
2609 std::vector<BOARD_ITEM*> thermalConnectionPads;
2610 std::vector<PAD*> noConnectionPads;
2611 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
2614 aFillPolys = aSmoothedOutline;
2641 aFillPolys, thermalRings );
2663 if( iterativeRefill )
2666 bool addedKeepoutHoles =
false;
2668 auto collectKeepoutHoles =
2669 [&](
ZONE* candidate )
2674 if( !isZoneFillKeepout( candidate, aLayer, zone_boundingbox ) )
2677 candidate->TransformSmoothedOutlineToPolygon( clearanceHoles, 0,
m_maxError,
2679 addedKeepoutHoles =
true;
2682 forEachBoardAndFootprintZone(
m_board, collectKeepoutHoles );
2684 if( addedKeepoutHoles )
2713 if( iterativeRefill )
2739 spokeTestIndex.
Build( testAreas );
2746 const VECTOR2I& testPt = spoke.CPoint( 3 );
2749 if( spokeTestIndex.
Contains( testPt, 1 ) )
2758 if( interval++ > 400 )
2771 if( &other != &spoke
2772 && other.PointInside( testPt, 1 )
2773 && spoke.PointInside( other.CPoint( 3 ), 1 ) )
2809 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
2811 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
2812 BOX2I islandExtents;
2814 for(
const VECTOR2I& pt : island.front().CPoints() )
2816 islandExtents.
Merge( pt );
2837 || !
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
2881 for(
BOARD_ITEM* item : thermalConnectionPads )
2895 bool knockoutsApplied =
false;
2897 if( iterativeRefill )
2908 knockoutsApplied =
true;
2921 if( knockoutsApplied )
2946 auto checkForCancel =
2949 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
2952 auto knockoutGraphicItem =
2955 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
2956 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2958 addKnockout( aItem, aLayer, 0,
true, clearanceHoles );
2967 knockoutGraphicItem( &footprint->Reference() );
2968 knockoutGraphicItem( &footprint->Value() );
2970 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2971 knockoutGraphicItem( item );
2979 knockoutGraphicItem( item );
2982 aFillPolys = aSmoothedOutline;
2987 auto collectKeepout =
2988 [&](
ZONE* candidate )
2990 if( !isZoneFillKeepout( candidate, aLayer, zone_boundingbox ) )
2993 appendZoneOutlineWithoutArcs( candidate, keepoutHoles );
2996 bool cancelledKeepoutScan =
false;
2998 forEachBoardAndFootprintZone(
3000 [&](
ZONE* keepout )
3002 if( cancelledKeepoutScan )
3007 cancelledKeepoutScan =
true;
3011 collectKeepout( keepout );
3014 if( cancelledKeepoutScan )
3060 debugLayer = aLayer;
3064 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
3072 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
3089 const std::vector<BOARD_ITEM*>& aSpokedPadsList,
3090 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
3109 if( !item->IsOnLayer( aLayer ) )
3112 int thermalReliefGap = 0;
3116 bool circular =
false;
3120 pad =
static_cast<PAD*
>( item );
3150 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
3151 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
3152 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
3154 if( spoke_w < aZone->GetMinThickness() )
3167 spoke_w = std::min( spoke_w,
via->GetWidth( aLayer ) );
3169 if( spoke_w < aZone->GetMinThickness() )
3188 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
3190 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
3193 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
3196 if( spoke_w < aZone->GetMinThickness() )
3205 int spoke_half_w = spoke_w / 2;
3208 BOX2I itemBB = item->GetBoundingBox();
3214 bool customSpokes =
false;
3218 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
3220 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
3222 customSpokes =
true;
3233 auto buildSpokesFromOrigin =
3240 auto intersectBBox =
3243 double dx = spokeAngle.
Cos();
3244 double dy = spokeAngle.
Sin();
3250 *spoke_side =
VECTOR2I( spoke_half_w, 0 );
3251 return KiROUND( 0.0, dy * half_size.
y );
3255 *spoke_side =
VECTOR2I( 0, spoke_half_w );
3256 return KiROUND( dx * half_size.
x, 0.0 );
3261 double dist_x = half_size.
x /
std::abs( dx );
3262 double dist_y = half_size.
y /
std::abs( dy );
3264 if( dist_x < dist_y )
3266 *spoke_side =
KiROUND( 0.0, spoke_half_w / (
ANGLE_90 - spokeAngle ).Sin() );
3267 return KiROUND( dx * dist_x, dy * dist_x );
3271 *spoke_side =
KiROUND( spoke_half_w / spokeAngle.
Sin(), 0.0 );
3272 return KiROUND( dx * dist_y, dy * dist_y );
3285 for(
const EDA_ANGLE& spokeAngle : angles )
3288 VECTOR2I intersection = intersectBBox( spokeAngle, &spoke_side );
3297 aSpokesList.push_back( std::move( spoke ) );
3309 thermalOutline = thermalPoly.
Outline( 0 );
3313 auto trimToOutline = [&](
SEG& aSegment )
3317 if( padOutline.
Intersect( aSegment, intersections ) )
3319 intersections.clear();
3322 if( thermalOutline.
Intersect( aSegment, intersections ) )
3324 aSegment.B = intersections.front().p;
3331 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
3333 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
3335 SEG seg( primitive->GetStart(), primitive->GetEnd() );
3340 seg.
A +=
pad->ShapePos( aLayer );
3341 seg.
B +=
pad->ShapePos( aLayer );
3355 if( trimToOutline( seg ) )
3357 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
3361 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
3362 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
3365 if( trimToOutline( segL ) && trimToOutline( segR ) )
3373 spoke.
Append( seg.
A + offset );
3374 spoke.
Append( seg.
A - offset );
3376 spoke.
Append( segL.
B + direction );
3377 spoke.
Append( seg.
B + direction );
3378 spoke.
Append( segR.
B + direction );
3381 aSpokesList.push_back( std::move( spoke ) );
3394 thermalSpokeAngle =
pad->GetThermalSpokeAngle();
3413 position =
pad->ShapePos( aLayer );
3414 orientation =
pad->GetOrientation();
3422 position =
via->GetPosition();
3427 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
3434 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
3436 if( thermalSpokeAngle !=
ANGLE_0 )
3439 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
3440 it->Rotate( thermalSpokeAngle );
3445 buildSpokesFromOrigin( spokesBox, thermalSpokeAngle );
3448 auto spokeIter = aSpokesList.rbegin();
3450 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
3452 spokeIter->Rotate( orientation );
3453 spokeIter->Move( position );
3458 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
3459 aSpokesList[ii].GenerateBBoxCache();
3465 const std::vector<BOARD_ITEM*>& aThermalConnectionPads,
3472 for(
BOARD_ITEM* item : aThermalConnectionPads )
3474 if( !item->IsOnLayer( aLayer ) )
3479 bool isCircular =
false;
3487 pad =
static_cast<PAD*
>( item );
3489 position =
pad->ShapePos( aLayer );
3495 padRadius = std::max( padSize.
x, padSize.
y ) / 2;
3504 int spokeMaxWidth = std::min( padSize.
x, padSize.
y );
3505 spokeWidth = std::min( spokeWidth, spokeMaxWidth );
3510 position =
via->GetPosition();
3512 padRadius =
via->GetWidth( aLayer ) / 2;
3521 spokeWidth = std::min( spokeWidth, padRadius * 2 );
3529 if( spokeWidth < aZone->GetMinThickness() )
3539 int ringInnerRadius = padRadius + thermalGap;
3540 int ringWidth = spokeWidth;
3553 pad->TransformShapeToPolygon( outerShape, aLayer, thermalGap + spokeWidth,
3557 pad->TransformShapeToPolygon( innerShape, aLayer, thermalGap,
3560 thermalRing = outerShape;
3592 int maxError =
m_board->GetDesignSettings().m_MaxError;
3608 hole_base.
Append( corner );
3609 corner.
x += hole_size;
3610 hole_base.
Append( corner );
3611 corner.
y += hole_size;
3612 hole_base.
Append( corner );
3614 hole_base.
Append( corner );
3634 #define SMOOTH_MIN_VAL_MM 0.02
3635 #define SMOOTH_SMALL_VAL_MM 0.04
3651 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
3654 maxError = std::max( maxError * 2, smooth_value / 20 );
3656 switch( smooth_level )
3668 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
3680 auto& defaultOffsets =
m_board->GetDesignSettings().m_ZoneLayerProperties;
3683 VECTOR2I offset = defaultOffsets[aLayer].hatching_offset.value_or(
VECTOR2I() );
3685 if( localOffsets.contains( aLayer ) && localOffsets.at( aLayer ).hatching_offset.has_value() )
3686 offset = localOffsets.at( aLayer ).hatching_offset.value();
3688 int x_offset = bbox.
GetX() - ( bbox.
GetX() ) % gridsize - gridsize;
3689 int y_offset = bbox.
GetY() - ( bbox.
GetY() ) % gridsize - gridsize;
3692 for(
int xx = x_offset; xx <= bbox.
GetRight(); xx += gridsize )
3694 for(
int yy = y_offset; yy <= bbox.
GetBottom(); yy += gridsize )
3705 hole.
Move(
VECTOR2I( offset.
x % gridsize, offset.
y % gridsize ) );
3718 deflated_thickness = std::max( deflated_thickness, maxError * 2 );
3740 if( area < minimal_hole_area )
3751 BOX2I thermalBBox = aThermalRings.
BBox();
3754 for(
int holeIdx = holes.
OutlineCount() - 1; holeIdx >= 0; holeIdx-- )
3764 for(
int ringIdx = 0; ringIdx < aThermalRings.
OutlineCount(); ringIdx++ )
3771 if( !holeBBox.
Contains( ringBBox ) )
3787 if( intersections.empty() )
3808 auto cacheKey = std::make_pair(
static_cast<const ZONE*
>( aZone ), aLayer );
3818 aFillPolys = it->second;
3831 auto evalRulesForItems =
3843 bool knockoutsApplied =
false;
3847 auto collectZoneKnockout =
3848 [&](
ZONE* otherZone )
3850 if( otherZone == aZone )
3853 if( !otherZone->GetLayerSet().test( aLayer ) )
3856 if( otherZone->IsTeardropArea() && otherZone->SameNet( aZone ) )
3859 if( !otherZone->HigherPriority( aZone ) )
3862 if( !otherZone->GetBoundingBox().Intersects( zoneBBox ) )
3865 if( !otherZone->HasFilledPolysForLayer( aLayer ) )
3868 std::shared_ptr<SHAPE_POLY_SET> otherFill = otherZone->GetFilledPolysList( aLayer );
3870 if( !otherFill || otherFill->OutlineCount() == 0 )
3873 if( otherZone->SameNet( aZone ) )
3875 sameNetKnockouts.
Append( *otherFill );
3880 aZone, otherZone, aLayer ) );
3883 otherZone, aLayer ) );
3891 diffNetKnockouts.
Append( inflatedFill );
3892 knockoutsApplied =
true;
3896 forEachBoardAndFootprintZone(
m_board, collectZoneKnockout );
3910 if( knockoutsApplied )
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.
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.
const ZONES & Zones() const
int GetCopperLayerCount() const
const FOOTPRINTS & Footprints() 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.
Y-stripe spatial index for efficient point-in-polygon containment testing.
bool Contains(const VECTOR2I &aPt, int aAccuracy=0) const
Test whether a point is inside the indexed polygon set.
void Build(const SHAPE_POLY_SET &aPolySet)
Build the spatial index from a SHAPE_POLY_SET's outlines and holes.
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)
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.
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.
void Insert(size_t aVertex, const VECTOR2I &aP)
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
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)
int ArcCount() const
Count the number of arc shapes present.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
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 CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, const SHAPE_POLY_SET::TASK_SUBMITTER &aSubmitter={})
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
void SetNeedRefill(bool aNeedRefill)
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
std::optional< int > GetLocalClearance() const override
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
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
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
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
bool HasKeepoutParametersSet() const
Accessor to determine if any keepout parameters are set.
double GetHatchSmoothingValue() const
bool GetDoNotAllowZoneFills() const
int GetHatchSmoothingLevel() const
bool IsOnCopperLayer() const override
unsigned GetAssignedPriority() const
bool SameNet(const ZONE *aOther) 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 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
#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