56 m_brdOutlinesValid( false ),
58 m_progressReporter( nullptr ),
75 wxASSERT_MSG(
m_commit, wxT(
"ZONE_FILLER must have a valid commit to call "
76 "SetProgressReporter" ) );
94 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
95 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
MD5_HASH> oldFillHashes;
96 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
101 connectivity->ClearRatsnest();
109 :
_(
"Building zone fills..." ) );
122 zone->CacheBoundingBox();
126 for(
PAD*
pad : footprint->Pads() )
135 for(
ZONE* zone : footprint->Zones() )
136 zone->CacheBoundingBox();
139 footprint->BuildCourtyardCaches();
144 auto findHighestPriorityZone = [&](
const BOX2I& aBBox,
const PCB_LAYER_ID aItemLayer,
146 const std::function<bool(
const ZONE* )> aTestFn ) ->
ZONE*
148 unsigned highestPriority = 0;
149 ZONE* highestPriorityZone =
nullptr;
154 if( zone->GetIsRuleArea() )
157 if( zone->GetAssignedPriority() < highestPriority )
160 if( !zone->IsOnLayer( aItemLayer ) )
164 if( zone->GetNumCorners() <= 2 )
167 if( !zone->GetBoundingBox().Intersects( aBBox ) )
170 if( !aTestFn( zone ) )
174 if( zone->GetAssignedPriority() > highestPriority || zone->GetNetCode() == aNetcode )
177 highestPriorityZone = zone;
181 return highestPriorityZone;
184 auto isInPourKeepoutArea = [&](
const BOX2I& aBBox,
const PCB_LAYER_ID aItemLayer,
189 if( !zone->GetIsRuleArea() )
192 if( !zone->GetDoNotAllowCopperPour() )
195 if( !zone->IsOnLayer( aItemLayer ) )
199 if( zone->GetNumCorners() <= 2 )
202 if( !zone->GetBoundingBox().Intersects( aBBox ) )
205 if( zone->Outline()->Contains( aTestPoint ) )
219 via->ClearZoneLayerOverrides();
221 if( !
via->GetRemoveUnconnected() )
226 int testRadius =
via->GetDrillValue() / 2 + 1;
227 unsigned netcode =
via->GetNetCode();
228 LSET layers =
via->GetLayerSet() & boardCuMask;
231 auto viaTestFn = [&](
const ZONE* aZone ) ->
bool
233 return aZone->Outline()->
Contains( center, -1, testRadius );
238 if( !
via->ConditionallyFlashed( layer ) )
241 if( isInPourKeepoutArea( bbox, layer, center ) )
247 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
261 for(
PAD*
pad : footprint->Pads() )
263 pad->ClearZoneLayerOverrides();
265 if( !
pad->GetRemoveUnconnected() )
270 unsigned netcode =
pad->GetNetCode();
271 LSET layers =
pad->GetLayerSet() & boardCuMask;
273 auto padTestFn = [&](
const ZONE* aZone ) ->
bool
275 return aZone->Outline()->
Contains( center );
280 if( !
pad->ConditionallyFlashed( layer ) )
283 if( isInPourKeepoutArea( bbox, layer, center ) )
289 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
300 for(
ZONE* zone : aZones )
303 if( zone->GetIsRuleArea() )
307 if( zone->GetNumCorners() <= 2 )
317 zone->BuildHashValue( layer );
318 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
321 toFill.emplace_back( std::make_pair( zone, layer ) );
330 auto check_fill_dependency =
338 if( aOtherZone->GetFillFlag( aLayer ) )
343 if( aOtherZone->GetIsRuleArea() )
347 if( aOtherZone->GetNumCorners() <= 2 )
351 if( !aOtherZone->GetLayerSet().test( aLayer ) )
358 if( aOtherZone->SameNet( aZone ) )
366 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
373 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
376 ZONE* zone = aFillItem.first;
381 for(
ZONE* otherZone : aZones )
383 if( otherZone == zone )
386 if( check_fill_dependency( zone, layer, otherZone ) )
401 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
403 if( !zoneLock.owns_lock() )
420 auto tesselate_lambda =
421 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
427 ZONE* zone = aFillItem.first;
430 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
432 if( !zoneLock.owns_lock() )
444 std::vector<std::pair<std::future<int>,
int>> returns;
445 returns.reserve( toFill.size() );
447 bool cancelled =
false;
451 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
452 returns.emplace_back( std::make_pair(
tp.submit( fill_lambda, fillItem ), 0 ) );
454 while( !cancelled && finished != 2 * toFill.size() )
456 for(
size_t ii = 0; ii < returns.size(); ++ii )
458 auto& ret = returns[ii];
463 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
465 if( status == std::future_status::ready )
467 if( ret.first.get() )
476 if( ret.second == 0 )
477 returns[ii].first =
tp.submit( fill_lambda, toFill[ii] );
478 else if( ret.second == 1 )
479 returns[ii].first =
tp.submit( tesselate_lambda, toFill[ii] );
484 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
498 for(
auto& ret : returns )
500 if( ret.first.valid() )
502 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
504 while( status != std::future_status::ready )
509 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
528 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
529 connectivity->SetProgressReporter(
nullptr );
534 for(
ZONE* zone : aZones )
537 if( zone->GetIsRuleArea() )
540 zone->SetIsFilled(
true );
546 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
549 bool allIslands =
true;
551 for(
const auto& [ layer, layerIslands ] : zoneIslands )
553 if( layerIslands.m_IsolatedOutlines.size()
554 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
564 for(
const auto& [ layer, layerIslands ] : zoneIslands )
569 if( layerIslands.m_IsolatedOutlines.empty() )
572 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
576 std::sort( islands.begin(), islands.end(), std::greater<int>() );
578 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
579 long long int minArea = zone->GetMinIslandArea();
582 for(
int idx : islands )
586 if( mode == ISLAND_REMOVAL_MODE::ALWAYS )
587 poly->DeletePolygonAndTriangulationData( idx,
false );
588 else if ( mode == ISLAND_REMOVAL_MODE::AREA && outline.
Area(
true ) < minArea )
589 poly->DeletePolygonAndTriangulationData( idx,
false );
591 zone->SetIsIsland( layer, idx );
594 poly->UpdateTriangulationDataHash();
595 zone->CalculateFilledArea();
604 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
606 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
611 for(
ZONE* zone : aZones )
621 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
628 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
633 [&](
int aStart,
int aEnd ) -> island_check_return
635 island_check_return retval;
637 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
639 auto [poly, minArea] = polys_to_check[ii];
641 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
646 double island_area = test_poly.
Area();
648 if( island_area < minArea )
661 if( intersection.
Area() < island_area / 2.0 )
662 retval.emplace_back( poly, jj );
669 auto island_returns =
tp.parallelize_loop( 0, polys_to_check.size(), island_lambda );
673 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
675 std::future<island_check_return>& ret = island_returns[ii];
679 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
681 while( status != std::future_status::ready )
691 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
699 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
701 std::future<island_check_return>& ret = island_returns[ii];
705 for(
auto& action_item : ret.get() )
706 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
710 for(
ZONE* zone : aZones )
711 zone->CalculateFilledArea();
716 bool outOfDate =
false;
718 for(
ZONE* zone : aZones )
721 if( zone->GetIsRuleArea() )
726 zone->BuildHashValue( layer );
728 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
735 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
736 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
770 if( aPad->
GetShape() == PAD_SHAPE::CUSTOM )
778 std::vector<VECTOR2I> convex_hull;
783 for(
const VECTOR2I& pt : convex_hull )
812 switch( aItem->
Type() )
819 if(
text->IsVisible() )
821 if(
text->IsKnockout() )
871 std::vector<PAD*>& aThermalConnectionPads,
872 std::vector<PAD*>& aNoConnectionPads )
883 for(
PAD*
pad : footprint->Pads() )
885 BOX2I padBBox =
pad->GetBoundingBox();
891 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
905 aNoConnectionPads.push_back(
pad );
911 connection = ZONE_CONNECTION::FULL;
915 constraint = bds.
m_DRCEngine->EvalZoneConnection(
pad, aZone, aLayer );
921 case ZONE_CONNECTION::THERMAL:
926 if(
pad->CanFlashLayer( aLayer ) )
928 aThermalConnectionPads.push_back(
pad );
931 else if(
pad->GetDrillSize().x > 0 )
938 case ZONE_CONNECTION::NONE:
947 if(
pad->FlashLayer( aLayer ) )
951 else if(
pad->GetDrillSize().x > 0 )
954 pad, aZone, aLayer );
959 holeClearance = padClearance;
982 const std::vector<PAD*>& aNoConnectionPads,
988 auto checkForCancel =
991 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1004 auto evalRulesForItems =
1018 auto knockoutPadClearance =
1023 bool hasHole = aPad->GetDrillSize().x > 0;
1024 bool flashLayer = aPad->FlashLayer( aLayer );
1025 bool platedHole = hasHole && aPad->GetAttribute() == PAD_ATTRIB::PTH;
1027 if( flashLayer || platedHole )
1030 aZone, aPad, aLayer ) );
1033 if( flashLayer && gap >= 0 )
1034 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1039 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
1043 aZone, aPad, aLayer ) );
1046 aZone, aPad, aLayer ) );
1053 for(
PAD*
pad : aNoConnectionPads )
1058 knockoutPadClearance(
pad );
1063 auto knockoutTrackClearance =
1066 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
1068 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
1074 aZone, aTrack, aLayer );
1087 aZone, aTrack, aLayer ) );
1094 if(
via->FlashLayer( aLayer ) && gap > 0 )
1096 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1101 aZone,
via, aLayer ) );
1106 aZone,
via, aLayer ) );
1111 int radius =
via->GetDrillValue() / 2;
1114 radius + gap + extra_margin,
1122 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1131 if( !track->IsOnLayer( aLayer ) )
1137 knockoutTrackClearance( track );
1142 auto knockoutGraphicClearance =
1150 bool sameNet = shapeNet == aZone->
GetNetCode();
1156 if( aItem->IsOnLayer( aLayer )
1158 || aItem->IsOnLayer(
Margin ) )
1160 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1162 bool ignoreLineWidths =
false;
1164 aZone, aItem, aLayer );
1166 if( aItem->IsOnLayer( aLayer ) && !sameNet )
1169 aZone, aItem, aLayer ) );
1171 else if( aItem->IsOnLayer(
Edge_Cuts ) )
1174 aZone, aItem, aLayer ) );
1175 ignoreLineWidths =
true;
1177 else if( aItem->IsOnLayer(
Margin ) )
1180 aZone, aItem, aLayer ) );
1185 gap += extra_margin;
1186 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
1192 auto knockoutCourtyardClearance =
1195 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
1198 aFootprint, aLayer );
1202 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
1215 knockoutCourtyardClearance( footprint );
1216 knockoutGraphicClearance( &footprint->Reference() );
1217 knockoutGraphicClearance( &footprint->Value() );
1219 std::set<PAD*> allowedNetTiePads;
1223 if( footprint->IsNetTie() )
1225 for(
PAD*
pad : footprint->Pads() )
1234 if(
pad->IsOnLayer( aLayer ) )
1235 allowedNetTiePads.insert(
pad );
1237 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
1239 if( other->IsOnLayer( aLayer ) )
1240 allowedNetTiePads.insert( other );
1246 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1251 BOX2I itemBBox = item->GetBoundingBox();
1253 if( !zone_boundingbox.
Intersects( itemBBox ) )
1256 bool skipItem =
false;
1258 if( item->IsOnLayer( aLayer ) )
1260 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
1262 for(
PAD*
pad : allowedNetTiePads )
1264 if(
pad->GetBoundingBox().Intersects( itemBBox )
1265 &&
pad->GetEffectiveShape()->Collide( itemShape.get() ) )
1274 knockoutGraphicClearance( item );
1283 knockoutGraphicClearance( item );
1288 auto knockoutZoneClearance =
1289 [&](
ZONE* aKnockout )
1292 if( !aKnockout->GetLayerSet().test( aLayer ) )
1295 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
1297 if( aKnockout->GetIsRuleArea() )
1300 aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0,
m_maxError,
1306 aZone, aKnockout, aLayer ) );
1309 aZone, aKnockout, aLayer ) );
1312 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
1328 if( otherZone->GetIsRuleArea() )
1330 if( otherZone->GetDoNotAllowCopperPour() && !aZone->
IsTeardropArea() )
1331 knockoutZoneClearance( otherZone );
1333 else if( otherZone->HigherPriority( aZone ) )
1335 if( !otherZone->SameNet( aZone ) )
1336 knockoutZoneClearance( otherZone );
1342 for(
ZONE* otherZone : footprint->Zones() )
1347 if( otherZone->GetIsRuleArea() )
1349 if( otherZone->GetDoNotAllowCopperPour() && !aZone->
IsTeardropArea() )
1350 knockoutZoneClearance( otherZone );
1352 else if( otherZone->HigherPriority( aZone ) )
1354 if( !otherZone->SameNet( aZone ) )
1355 knockoutZoneClearance( otherZone );
1373 auto knockoutZoneOutline =
1374 [&](
ZONE* aKnockout )
1377 if( !aKnockout->GetLayerSet().test( aLayer ) )
1380 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
1396 if( otherZone->SameNet( aZone )
1400 if( !otherZone->IsTeardropArea() )
1401 knockoutZoneOutline( otherZone );
1407 for(
ZONE* otherZone : footprint->Zones() )
1409 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
1412 if( !otherZone->IsTeardropArea() )
1413 knockoutZoneOutline( otherZone );
1420#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
1421 { if( m_debugZoneFiller && aDebugLayer == b ) \
1423 m_board->SetLayerName( b, c ); \
1424 SHAPE_POLY_SET d = a; \
1425 d.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); \
1465 CORNER_STRATEGY fastCornerStrategy = CORNER_STRATEGY::CHAMFER_ALL_CORNERS;
1468 std::vector<PAD*> thermalConnectionPads;
1469 std::vector<PAD*> noConnectionPads;
1470 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
1473 aFillPolys = aSmoothedOutline;
1510 static const bool USE_BBOX_CACHES =
true;
1537 const VECTOR2I& testPt = spoke.CPoint( 3 );
1540 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
1549 if( interval++ > 400 )
1562 if( &other != &spoke
1563 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
1564 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
1594 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
1596 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
1597 BOX2I islandExtents;
1599 for(
const VECTOR2I& pt : island.front().CPoints() )
1601 islandExtents.
Merge( pt );
1620 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1644 for(
PAD*
pad : thermalConnectionPads )
1673 auto checkForCancel =
1676 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1679 auto knockoutGraphicClearance =
1682 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
1683 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1686 aZone, aItem, aLayer );
1697 knockoutGraphicClearance( &footprint->Reference() );
1698 knockoutGraphicClearance( &footprint->Value() );
1700 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1701 knockoutGraphicClearance( item );
1709 knockoutGraphicClearance( item );
1712 aFillPolys = aSmoothedOutline;
1717 if( !keepout->GetIsRuleArea() )
1720 if( keepout->GetDoNotAllowCopperPour() && keepout->IsOnLayer( aLayer ) )
1722 if( keepout->GetBoundingBox().Intersects( zone_boundingbox ) )
1736 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1765 debugLayer = aLayer;
1769 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
1777 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
1794 const std::vector<PAD*>& aSpokedPadsList,
1795 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
1807 for(
PAD*
pad : aSpokedPadsList )
1810 if( !
pad->IsOnLayer( aLayer ) )
1814 int thermalReliefGap = constraint.
GetValue().
Min();
1822 int spoke_max_allowed_w = std::min(
pad->GetSize().x,
pad->GetSize().y );
1824 spoke_w = std::max( spoke_w, constraint.
Value().
Min() );
1825 spoke_w = std::min( spoke_w, constraint.
Value().
Max() );
1828 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
1831 if( spoke_w < aZone->GetMinThickness() )
1834 int spoke_half_w = spoke_w / 2;
1837 BOX2I itemBB =
pad->GetBoundingBox();
1843 bool customSpokes =
false;
1845 if(
pad->GetShape() == PAD_SHAPE::CUSTOM )
1847 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives() )
1849 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
1851 customSpokes =
true;
1862 auto buildSpokesFromOrigin =
1863 [&](
const BOX2I& box )
1865 for(
int i = 0; i < 4; i++ )
1872 spoke.
Append( +spoke_half_w, -spoke_half_w );
1873 spoke.
Append( -spoke_half_w, -spoke_half_w );
1874 spoke.
Append( -spoke_half_w, box.GetBottom() );
1875 spoke.
Append( 0, box.GetBottom() );
1876 spoke.
Append( +spoke_half_w, box.GetBottom() );
1880 spoke.
Append( +spoke_half_w, +spoke_half_w );
1881 spoke.
Append( -spoke_half_w, +spoke_half_w );
1882 spoke.
Append( -spoke_half_w, box.GetTop() );
1883 spoke.
Append( 0, box.GetTop() );
1884 spoke.
Append( +spoke_half_w, box.GetTop() );
1888 spoke.
Append( -spoke_half_w, +spoke_half_w );
1889 spoke.
Append( -spoke_half_w, -spoke_half_w );
1890 spoke.
Append( box.GetRight(), -spoke_half_w );
1891 spoke.
Append( box.GetRight(), 0 );
1892 spoke.
Append( box.GetRight(), +spoke_half_w );
1896 spoke.
Append( +spoke_half_w, +spoke_half_w );
1897 spoke.
Append( +spoke_half_w, -spoke_half_w );
1898 spoke.
Append( box.GetLeft(), -spoke_half_w );
1899 spoke.
Append( box.GetLeft(), 0 );
1900 spoke.
Append( box.GetLeft(), +spoke_half_w );
1905 aSpokesList.push_back( std::move( spoke ) );
1914 pad->TransformShapeToPolygon( thermalPoly, aLayer, thermalReliefGap +
epsilon,
1918 thermalOutline = thermalPoly.
Outline( 0 );
1920 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives() )
1922 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
1924 SEG seg( primitive->GetStart(), primitive->GetEnd() );
1929 seg.
A +=
pad->ShapePos();
1930 seg.
B +=
pad->ShapePos();
1937 if( thermalOutline.
Intersect( seg, intersections ) )
1939 seg.
B = intersections.front().p;
1941 VECTOR2I offset = ( seg.
B - seg.
A ).Perpendicular().
Resize( spoke_half_w );
1944 spoke.
Append( seg.
A + offset );
1945 spoke.
Append( seg.
A - offset );
1946 spoke.
Append( seg.
B - offset );
1948 spoke.
Append( seg.
B + offset );
1951 aSpokesList.push_back( std::move( spoke ) );
1958 else if( (
pad->GetOrientation() +
pad->GetThermalSpokeAngle() ).IsCardinal() )
1960 BOX2I spokesBox =
pad->GetBoundingBox();
1966 buildSpokesFromOrigin( spokesBox );
1968 auto spokeIter = aSpokesList.rbegin();
1970 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
1971 spokeIter->Move(
pad->ShapePos() );
1975 else if(
pad->GetSizeX() ==
pad->GetSizeY() &&
pad->GetShape() != PAD_SHAPE::CUSTOM )
1990 buildSpokesFromOrigin( spokesBox );
1992 auto spokeIter = aSpokesList.rbegin();
1994 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
1996 spokeIter->Rotate(
pad->GetOrientation() +
pad->GetThermalSpokeAngle() );
1997 spokeIter->Move(
pad->ShapePos() );
2022 buildSpokesFromOrigin( spokesBox );
2024 BOX2I realBBox =
pad->GetBoundingBox();
2027 auto spokeIter = aSpokesList.rbegin();
2029 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
2031 spokeIter->Rotate(
pad->GetOrientation() +
pad->GetThermalSpokeAngle() );
2032 spokeIter->
Move(
pad->ShapePos() );
2034 VECTOR2I origin_p = spokeIter->GetPoint( 0 );
2035 VECTOR2I origin_m = spokeIter->GetPoint( 1 );
2036 VECTOR2I origin = ( origin_p + origin_m ) / 2;
2037 VECTOR2I end_m = spokeIter->GetPoint( 2 );
2038 VECTOR2I end = spokeIter->GetPoint( 3 );
2039 VECTOR2I end_p = spokeIter->GetPoint( 4 );
2041 ClipLine( &realBBox, origin_p.
x, origin_p.
y, end_p.
x, end_p.
y );
2042 ClipLine( &realBBox, origin_m.
x, origin_m.
y, end_m.
x, end_m.
y );
2043 ClipLine( &realBBox, origin.
x, origin.
y, end.
x, end.
y );
2045 spokeIter->SetPoint( 2, end_m );
2046 spokeIter->SetPoint( 3, end );
2047 spokeIter->SetPoint( 4, end_p );
2055 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
2056 aSpokesList[ii].GenerateBBoxCache();
2092 hole_base.
Append( corner );
2093 corner.
x += hole_size;
2094 hole_base.
Append( corner );
2095 corner.
y += hole_size;
2096 hole_base.
Append( corner );
2098 hole_base.
Append( corner );
2118 #define SMOOTH_MIN_VAL_MM 0.02
2119 #define SMOOTH_SMALL_VAL_MM 0.04
2135 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
2138 maxError = std::max( maxError * 2, smooth_value / 20 );
2140 switch( smooth_level )
2152 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
2164 for(
int xx = 0; ; xx++ )
2166 int xpos = xx * gridsize;
2171 for(
int yy = 0; ; yy++ )
2173 int ypos = yy * gridsize;
2202 CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
2207 deflatedOutline.
Deflate( outline_margin, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
2219 int min_apron_radius = ( aZone->
GetHatchGap() * 10 ) / 19;
2228 &&
via->IsOnLayer( aLayer )
2229 &&
via->GetBoundingBox().Intersects( zone_boundingbox ) )
2231 int r = std::max( min_apron_radius,
2232 via->GetDrillValue() / 2 + outline_margin );
2242 for(
PAD*
pad : footprint->Pads() )
2245 &&
pad->IsOnLayer( aLayer )
2246 &&
pad->GetBoundingBox().Intersects( zone_boundingbox ) )
2253 int pad_width = std::min(
pad->GetSize().x,
pad->GetSize().y );
2254 int slot_width = std::min(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
2255 int min_annular_ring_width = ( pad_width - slot_width ) / 2;
2256 int clearance = std::max( min_apron_radius - pad_width / 2,
2257 outline_margin - min_annular_ring_width );
2259 clearance = std::max( 0, clearance - linethickness / 2 );
2260 pad->TransformShapeToPolygon( aprons, aLayer, clearance, maxError,
2276 if( area < minimal_hole_area )
constexpr int ARC_HIGH_DEF
constexpr EDA_IU_SCALE pcbIUScale
@ ZLO_FORCE_NO_ZONE_CONNECTION
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...
void SetParentGroup(PCB_GROUP *aGroup)
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.
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
const ZONES & Zones() const
int GetMaxClearanceValue() const
Returns the maximum clearance value for any object on the board.
int GetCopperLayerCount() const
const FOOTPRINTS & Footprints() const
const TRACKS & Tracks() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
const DRAWINGS & Drawings() const
const Vec & GetPosition() const
void Offset(coord_type dx, coord_type dy)
size_type GetHeight() const
bool Intersects(const BOX2< Vec > &aRect) const
size_type GetWidth() const
void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Represent a set of changes (additions, deletions or modifications) of a data model (e....
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
MINOPTMAX< int > & Value()
const MINOPTMAX< int > & GetValue() const
ZONE_CONNECTION m_ZoneConnection
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)
Checks the 'do not show again' setting for the dialog.
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
Shows the 'do not show again' checkbox.
LSET is a set of PCB_LAYER_IDs.
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
static LSET InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
void SetOffset(const VECTOR2I &aOffset)
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
void SetPosition(const VECTOR2I &aPos) override
PAD_SHAPE GetShape() const
PADSTACK::CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
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 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 TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
A progress reporter interface for use in multi-threaded environments.
virtual bool IsCancelled() const =0
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual void AdvancePhase()=0
Use the next available virtual zone of the dialog progress bar.
virtual void AdvanceProgress()=0
Increment the progress bar length (inside the current virtual zone).
virtual void SetMaxProgress(int aMaxProgress)=0
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
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.
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.
std::vector< INTERSECTION > INTERSECTIONS
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.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
SHAPE_POLY_SET Chamfer(int aDistance)
Return a chamfered version of the polygon set.
void BooleanSubtract(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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 BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
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(POLYGON_MODE aFastMode)
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFastMo...
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 BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon set.
void Move(const VECTOR2I &aVector) override
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
SHAPE_POLY_SET CloneDropTriangulation() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
void buildCopperItemClearances(const ZONE *aZone, PCB_LAYER_ID aLayer, const std::vector< PAD * > &aNoConnectionPads, SHAPE_POLY_SET &aHoles)
Removes clearance from the shape for copper items which share the zone's layer but are not connected ...
void addKnockout(PAD *aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad.
ZONE_FILLER(BOARD *aBoard, COMMIT *aCommit)
void buildThermalSpokes(const ZONE *box, PCB_LAYER_ID aLayer, const std::vector< PAD * > &aSpokedPadsList, std::deque< SHAPE_LINE_CHAIN > &aSpokes)
Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone.
void subtractHigherPriorityZones(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawFill)
Removes the outlines of higher-proirity zones with the same net.
SHAPE_POLY_SET m_boardOutline
bool addHatchFillTypeOnZone(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET &aFillPolys)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
void SetProgressReporter(PROGRESS_REPORTER *aReporter)
void knockoutThermalReliefs(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFill, std::vector< PAD * > &aThermalConnectionPads, std::vector< PAD * > &aNoConnectionPads)
Removes thermal reliefs from the shape for any pads connected to the zone.
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 *aZone, PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aSmoothedOutline, SHAPE_POLY_SET &aFillPolys)
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)
int GetHatchBorderAlgorithm() const
std::optional< int > GetLocalClearance() const override
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 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
@ EDGE_CLEARANCE_CONSTRAINT
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
@ THERMAL_RELIEF_GAP_CONSTRAINT
@ HOLE_CLEARANCE_CONSTRAINT
@ PHYSICAL_CLEARANCE_CONSTRAINT
a few functions useful in geometry calculations.
bool ClipLine(const BOX2I *aClipBox, int &x1, int &y1, int &x2, int &y2)
Test if any part of a line falls within the bounds of a rectangle.
double m_ExtraClearance
When filling zones, we add an extra amount of clearance to each zone to ensure that rounding errors d...
bool m_DebugZoneFiller
A mode that dumps the various stages of a F_Cu fill into In1_Cu through In9_Cu.
This file is part of the common library.
PCB_LAYER_ID
A quick note on layer IDs:
constexpr int mmToIU(double mm) const
A struct recording the isolated and single-pad islands within a zone.
BS::thread_pool thread_pool
thread_pool & GetKiCadThreadPool()
Get a reference to the current 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_TARGET_T
class PCB_TARGET, a target (graphic item)
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
#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.