60 RESULTS(
int aOutline1,
int aOutline2,
int aVertex1,
int aVertex2 ) :
107 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
110 auto check_pt = [&](
VERTEX* p )
112 VECTOR2D diff( p->x - aPt->
x, p->y - aPt->
y );
115 if( dist2 > 0 && dist2 < limit2 && dist2 < min_dist && p->isEar(
true ) )
124 while( p && p->
z <= maxZ )
132 while( p && p->
z >= minZ )
147 std::set<VERTEX*> visited;
160 if( ( visited.empty() || !visited.contains( p ) ) && ( q =
getPoint( p ) ) )
164 if( !visited.contains( q ) &&
166 p->
i, q->
i ).second )
170 visited.insert( p->
prev );
172 visited.insert( p->
next );
175 visited.insert( q->
prev );
177 visited.insert( q->
next );
201 m_brdOutlinesValid( false ),
203 m_progressReporter( nullptr ),
205 m_worstClearance( 0 )
220 wxASSERT_MSG(
m_commit, wxT(
"ZONE_FILLER must have a valid commit to call "
221 "SetProgressReporter" ) );
239 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
240 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
HASH_128> oldFillHashes;
241 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
246 connectivity->ClearRatsnest();
254 :
_(
"Building zone fills..." ) );
267 zone->CacheBoundingBox();
271 for(
PAD*
pad : footprint->Pads() )
275 pad->BuildEffectiveShapes();
280 for(
ZONE* zone : footprint->Zones() )
281 zone->CacheBoundingBox();
284 footprint->BuildCourtyardCaches();
285 footprint->BuildNetTieCache();
290 auto findHighestPriorityZone =
292 const std::function<bool(
const ZONE* )>& testFn ) ->
ZONE*
294 unsigned highestPriority = 0;
295 ZONE* highestPriorityZone =
nullptr;
300 if( zone->GetIsRuleArea() )
303 if( zone->GetAssignedPriority() < highestPriority )
306 if( !zone->IsOnLayer( itemLayer ) )
310 if( zone->GetNumCorners() <= 2 )
313 if( !zone->GetBoundingBox().Intersects( bbox ) )
316 if( !testFn( zone ) )
320 if( zone->GetAssignedPriority() > highestPriority
321 || zone->GetNetCode() == netcode )
324 highestPriorityZone = zone;
328 return highestPriorityZone;
331 auto isInPourKeepoutArea =
336 if( !zone->GetIsRuleArea() )
339 if( !zone->HasKeepoutParametersSet() )
342 if( !zone->GetDoNotAllowZoneFills() )
345 if( !zone->IsOnLayer( itemLayer ) )
349 if( zone->GetNumCorners() <= 2 )
352 if( !zone->GetBoundingBox().Intersects( bbox ) )
355 if( zone->Outline()->Contains( testPoint ) )
371 via->ClearZoneLayerOverrides();
373 if( !
via->GetRemoveUnconnected() )
378 int holeRadius =
via->GetDrillValue() / 2 + 1;
379 int netcode =
via->GetNetCode();
380 LSET layers =
via->GetLayerSet() & boardCuMask;
384 [&](
const ZONE* aZone ) ->
bool
391 if( !
via->ConditionallyFlashed( layer ) )
394 if( isInPourKeepoutArea( bbox, layer,
center ) )
400 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
414 for(
PAD*
pad : footprint->Pads() )
416 pad->ClearZoneLayerOverrides();
418 if( !
pad->GetRemoveUnconnected() )
423 int netcode =
pad->GetNetCode();
424 LSET layers =
pad->GetLayerSet() & boardCuMask;
427 [&](
const ZONE* aZone ) ->
bool
434 if( !
pad->ConditionallyFlashed( layer ) )
437 if( isInPourKeepoutArea( bbox, layer,
center ) )
443 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
454 for(
ZONE* zone : aZones )
457 if( zone->GetIsRuleArea() )
461 if( zone->GetNumCorners() <= 2 )
471 zone->BuildHashValue( layer );
472 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
475 toFill.emplace_back( std::make_pair( zone, layer ) );
484 auto check_fill_dependency =
492 if( aOtherZone->GetFillFlag( aLayer ) )
497 if( aOtherZone->GetIsRuleArea() )
501 if( aOtherZone->GetNumCorners() <= 2 )
505 if( !aOtherZone->GetLayerSet().test( aLayer ) )
512 if( aOtherZone->SameNet( aZone ) )
520 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
527 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
530 ZONE* zone = aFillItem.first;
535 for(
ZONE* otherZone : aZones )
537 if( otherZone == zone )
540 if( check_fill_dependency( zone, layer, otherZone ) )
555 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
557 if( !zoneLock.owns_lock() )
574 auto tesselate_lambda =
575 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
581 ZONE* zone = aFillItem.first;
584 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
586 if( !zoneLock.owns_lock() )
598 std::vector<std::pair<std::future<int>,
int>> returns;
599 returns.reserve( toFill.size() );
601 bool cancelled =
false;
605 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
606 returns.emplace_back( std::make_pair(
tp.submit( fill_lambda, fillItem ), 0 ) );
608 while( !cancelled && finished != 2 * toFill.size() )
610 for(
size_t ii = 0; ii < returns.size(); ++ii )
612 auto& ret = returns[ii];
617 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
619 if( status == std::future_status::ready )
621 if( ret.first.get() )
630 if( ret.second == 0 )
631 returns[ii].first =
tp.submit( fill_lambda, toFill[ii] );
632 else if( ret.second == 1 )
633 returns[ii].first =
tp.submit( tesselate_lambda, toFill[ii] );
638 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
652 for(
auto& ret : returns )
654 if( ret.first.valid() )
656 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
658 while( status != std::future_status::ready )
663 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
682 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
683 connectivity->SetProgressReporter(
nullptr );
688 for(
ZONE* zone : aZones )
691 if( zone->GetIsRuleArea() )
694 zone->SetIsFilled(
true );
700 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
703 bool allIslands =
true;
705 for(
const auto& [ layer, layerIslands ] : zoneIslands )
707 if( layerIslands.m_IsolatedOutlines.size()
708 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
718 for(
const auto& [ layer, layerIslands ] : zoneIslands )
723 if( layerIslands.m_IsolatedOutlines.empty() )
726 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
730 std::sort( islands.begin(), islands.end(), std::greater<int>() );
732 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
733 long long int minArea = zone->GetMinIslandArea();
736 for(
int idx : islands )
740 if( mode == ISLAND_REMOVAL_MODE::ALWAYS )
741 poly->DeletePolygonAndTriangulationData( idx,
false );
742 else if ( mode == ISLAND_REMOVAL_MODE::AREA && outline.
Area(
true ) < minArea )
743 poly->DeletePolygonAndTriangulationData( idx,
false );
745 zone->SetIsIsland( layer, idx );
748 poly->UpdateTriangulationDataHash();
749 zone->CalculateFilledArea();
758 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
760 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
765 for(
ZONE* zone : aZones )
775 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
782 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
787 [&](
int aStart,
int aEnd ) -> island_check_return
789 island_check_return retval;
791 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
793 auto [poly, minArea] = polys_to_check[ii];
795 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
800 double island_area = test_poly.
Area();
802 if( island_area < minArea )
814 if( intersection.
Area() < island_area / 2.0 )
815 retval.emplace_back( poly, jj );
822 auto island_returns =
tp.parallelize_loop( 0, polys_to_check.size(), island_lambda );
826 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
828 std::future<island_check_return>& ret = island_returns[ii];
832 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
834 while( status != std::future_status::ready )
844 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
852 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
854 std::future<island_check_return>& ret = island_returns[ii];
858 for(
auto& action_item : ret.get() )
859 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
863 for(
ZONE* zone : aZones )
864 zone->CalculateFilledArea();
869 bool outOfDate =
false;
871 for(
ZONE* zone : aZones )
874 if( zone->GetIsRuleArea() )
879 zone->BuildHashValue( layer );
881 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
889 KIDIALOG dlg( aParent,
_(
"Prototype zone fill enabled. Disable setting and refill?" ),
890 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
898 else if( !outOfDate )
906 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
907 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
950 std::vector<VECTOR2I> convex_hull;
955 for(
const VECTOR2I& pt : convex_hull )
986 minorAxis = std::min( padSize.
x, padSize.
y );
992 minorAxis =
via->GetWidth( aLayer );
1006 switch( aItem->
Type() )
1013 if(
text->IsVisible() )
1015 if(
text->IsKnockout() )
1065 std::vector<BOARD_ITEM*>& aThermalConnectionPads,
1066 std::vector<PAD*>& aNoConnectionPads )
1072 std::shared_ptr<SHAPE> padShape;
1078 for(
PAD*
pad : footprint->Pads() )
1080 if( !
pad->IsOnLayer( aLayer ) )
1083 BOX2I padBBox =
pad->GetBoundingBox();
1089 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1096 noConnection =
true;
1103 aNoConnectionPads.push_back(
pad );
1111 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1113 aThermalConnectionPads.push_back(
pad );
1120 connection = ZONE_CONNECTION::FULL;
1124 constraint = bds.
m_DRCEngine->EvalZoneConnection(
pad, aZone, aLayer );
1128 if( connection == ZONE_CONNECTION::THERMAL && !
pad->CanFlashLayer( aLayer ) )
1129 connection = ZONE_CONNECTION::NONE;
1131 switch( connection )
1133 case ZONE_CONNECTION::THERMAL:
1134 padShape =
pad->GetEffectiveShape( aLayer, FLASHING::ALWAYS_FLASHED );
1136 if( aFill.
Collide( padShape.get(), 0 ) )
1142 aThermalConnectionPads.push_back(
pad );
1148 case ZONE_CONNECTION::NONE:
1157 if(
pad->FlashLayer( aLayer ) )
1161 else if(
pad->GetDrillSize().x > 0 )
1164 pad, aZone, aLayer );
1169 holeClearance = padClearance;
1186 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1194 if( !
via->IsOnLayer( aLayer ) )
1197 BOX2I viaBBox =
via->GetBoundingBox();
1203 bool noConnection =
via->GetNetCode() != aZone->
GetNetCode();
1208 aThermalConnectionPads.push_back(
via );
1223 const std::vector<PAD*>& aNoConnectionPads,
1229 auto checkForCancel =
1232 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1245 auto evalRulesForItems =
1259 auto knockoutPadClearance =
1264 bool hasHole = aPad->GetDrillSize().x > 0;
1265 bool flashLayer = aPad->FlashLayer( aLayer );
1266 bool platedHole = hasHole && aPad->GetAttribute() == PAD_ATTRIB::PTH;
1268 if( flashLayer || platedHole )
1271 aZone, aPad, aLayer ) );
1274 if( flashLayer && gap >= 0 )
1275 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1280 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
1284 aZone, aPad, aLayer ) );
1287 aZone, aPad, aLayer ) );
1294 for(
PAD*
pad : aNoConnectionPads )
1299 knockoutPadClearance(
pad );
1304 auto knockoutTrackClearance =
1307 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
1309 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
1315 aZone, aTrack, aLayer );
1328 aZone, aTrack, aLayer ) );
1335 if(
via->FlashLayer( aLayer ) && gap > 0 )
1337 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1342 aZone,
via, aLayer ) );
1347 aZone,
via, aLayer ) );
1355 radius + gap + extra_margin,
1363 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1372 if( !track->IsOnLayer( aLayer ) )
1378 knockoutTrackClearance( track );
1383 auto knockoutGraphicClearance =
1391 bool sameNet = shapeNet == aZone->
GetNetCode();
1397 if( aItem->IsOnLayer( aLayer )
1399 || aItem->IsOnLayer(
Margin ) )
1401 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1403 bool ignoreLineWidths =
false;
1405 aZone, aItem, aLayer );
1407 if( aItem->IsOnLayer( aLayer ) && !sameNet )
1410 aZone, aItem, aLayer ) );
1412 else if( aItem->IsOnLayer(
Edge_Cuts ) )
1415 aZone, aItem, aLayer ) );
1416 ignoreLineWidths =
true;
1418 else if( aItem->IsOnLayer(
Margin ) )
1421 aZone, aItem, aLayer ) );
1426 gap += extra_margin;
1427 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
1433 auto knockoutCourtyardClearance =
1436 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
1439 aFootprint, aLayer );
1443 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
1456 knockoutCourtyardClearance( footprint );
1457 knockoutGraphicClearance( &footprint->Reference() );
1458 knockoutGraphicClearance( &footprint->Value() );
1460 std::set<PAD*> allowedNetTiePads;
1464 if( footprint->IsNetTie() )
1466 for(
PAD*
pad : footprint->Pads() )
1475 if(
pad->IsOnLayer( aLayer ) )
1476 allowedNetTiePads.insert(
pad );
1478 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
1480 if( other->IsOnLayer( aLayer ) )
1481 allowedNetTiePads.insert( other );
1487 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1492 BOX2I itemBBox = item->GetBoundingBox();
1494 if( !zone_boundingbox.
Intersects( itemBBox ) )
1497 bool skipItem =
false;
1499 if( item->IsOnLayer( aLayer ) )
1501 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
1503 for(
PAD*
pad : allowedNetTiePads )
1505 if(
pad->GetBoundingBox().Intersects( itemBBox )
1506 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
1515 knockoutGraphicClearance( item );
1524 knockoutGraphicClearance( item );
1529 auto knockoutZoneClearance =
1530 [&](
ZONE* aKnockout )
1533 if( !aKnockout->GetLayerSet().test( aLayer ) )
1536 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
1538 if( aKnockout->GetIsRuleArea() )
1541 aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0,
m_maxError,
1547 aZone, aKnockout, aLayer ) );
1550 aZone, aKnockout, aLayer ) );
1553 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
1569 if( otherZone->GetIsRuleArea() )
1571 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
1572 knockoutZoneClearance( otherZone );
1574 else if( otherZone->HigherPriority( aZone ) )
1576 if( !otherZone->SameNet( aZone ) )
1577 knockoutZoneClearance( otherZone );
1583 for(
ZONE* otherZone : footprint->Zones() )
1588 if( otherZone->GetIsRuleArea() )
1590 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
1591 knockoutZoneClearance( otherZone );
1593 else if( otherZone->HigherPriority( aZone ) )
1595 if( !otherZone->SameNet( aZone ) )
1596 knockoutZoneClearance( otherZone );
1614 auto knockoutZoneOutline =
1615 [&](
ZONE* aKnockout )
1618 if( !aKnockout->GetLayerSet().test( aLayer ) )
1621 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
1637 if( otherZone->SameNet( aZone )
1641 if( !otherZone->IsTeardropArea() )
1642 knockoutZoneOutline( otherZone );
1648 for(
ZONE* otherZone : footprint->Zones() )
1650 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
1653 if( !otherZone->IsTeardropArea() )
1654 knockoutZoneOutline( otherZone );
1672 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
1684 insertion_points[result.m_outline1].push_back( { result.m_vertex1, pt1 } );
1685 insertion_points[result.m_outline1].push_back( { result.m_vertex1, pt2 } );
1688 for(
auto& [outline, vertices] : insertion_points )
1696 std::stable_sort( vertices.begin(), vertices.end(),
1697 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
1699 return a.first > b.first;
1702 for(
const auto& [vertex, pt] : vertices )
1703 line.
Insert( vertex + 1, pt );
1708#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
1709 { if( m_debugZoneFiller && aDebugLayer == b ) \
1711 m_board->SetLayerName( b, c ); \
1712 SHAPE_POLY_SET d = a; \
1748 CORNER_STRATEGY fastCornerStrategy = CORNER_STRATEGY::CHAMFER_ALL_CORNERS;
1751 std::vector<BOARD_ITEM*> thermalConnectionPads;
1752 std::vector<PAD*> noConnectionPads;
1753 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
1756 aFillPolys = aSmoothedOutline;
1793 static const bool USE_BBOX_CACHES =
true;
1820 const VECTOR2I& testPt = spoke.CPoint( 3 );
1823 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
1832 if( interval++ > 400 )
1845 if( &other != &spoke
1846 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
1847 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
1877 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
1879 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
1880 BOX2I islandExtents;
1882 for(
const VECTOR2I& pt : island.front().CPoints() )
1884 islandExtents.
Merge( pt );
1903 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN
1940 for(
BOARD_ITEM* item : thermalConnectionPads )
1971 auto checkForCancel =
1974 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1977 auto knockoutGraphicItem =
1980 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
1981 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1983 addKnockout( aItem, aLayer, 0,
true, clearanceHoles );
1992 knockoutGraphicItem( &footprint->Reference() );
1993 knockoutGraphicItem( &footprint->Value() );
1995 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1996 knockoutGraphicItem( item );
2004 knockoutGraphicItem( item );
2007 aFillPolys = aSmoothedOutline;
2010 auto subtractKeepout =
2011 [&](
ZONE* candidate )
2013 if( !candidate->GetIsRuleArea() )
2016 if( !candidate->HasKeepoutParametersSet() )
2019 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
2021 if( candidate->GetBoundingBox().Intersects( zone_boundingbox ) )
2023 if( candidate->Outline()->ArcCount() == 0 )
2042 subtractKeepout( keepout );
2050 for(
ZONE* keepout : footprint->Zones() )
2051 subtractKeepout( keepout );
2063 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
2092 debugLayer = aLayer;
2096 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
2104 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
2121 const std::vector<BOARD_ITEM*>& aSpokedPadsList,
2122 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
2129 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
2141 if( !item->IsOnLayer( aLayer ) )
2144 int thermalReliefGap = 0;
2148 bool circular =
false;
2152 pad =
static_cast<PAD*
>( item );
2155 if(
pad->GetShape( aLayer) == PAD_SHAPE::CIRCLE
2156 || (
pad->GetShape( aLayer ) == PAD_SHAPE::OVAL && padSize.
x == padSize.
y ) )
2171 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
2176 if( thermalReliefGap < 0 )
2190 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
2192 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
2195 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
2198 if( spoke_w < aZone->GetMinThickness() )
2207 int spoke_half_w = spoke_w / 2;
2210 BOX2I itemBB = item->GetBoundingBox();
2216 bool customSpokes =
false;
2218 if(
pad &&
pad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2220 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2222 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
2224 customSpokes =
true;
2235 auto buildSpokesFromOrigin =
2242 auto intersectLineBox =
2245 double dx = direction.
x;
2246 double dy = direction.y;
2250 if( direction.x == 0 )
2252 else if( direction.y == 0 )
2257 double tx = std::min( half_size.
x /
std::abs( dx ),
2259 return VECTOR2I( dx * tx, dy * tx );
2271 for(
const EDA_ANGLE& spokeAngle : angles )
2273 VECTOR2D direction( spokeAngle.Cos(), spokeAngle.Sin() );
2276 VECTOR2I intersection = intersectLineBox( direction );
2286 aSpokesList.push_back( std::move( spoke ) );
2295 pad->TransformShapeToPolygon( thermalPoly, aLayer, thermalReliefGap +
epsilon,
2299 thermalOutline = thermalPoly.
Outline( 0 );
2303 auto trimToOutline = [&](
SEG& aSegment )
2307 if( padOutline.
Intersect( aSegment, intersections ) )
2309 intersections.clear();
2312 if( thermalOutline.
Intersect( aSegment, intersections ) )
2314 aSegment.B = intersections.front().p;
2321 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2323 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
2325 SEG seg( primitive->GetStart(), primitive->GetEnd() );
2330 seg.
A +=
pad->ShapePos( aLayer );
2331 seg.
B +=
pad->ShapePos( aLayer );
2345 if( trimToOutline( seg ) )
2347 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
2351 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
2352 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
2355 if( trimToOutline( segL ) && trimToOutline( segR ) )
2363 spoke.
Append( seg.
A + offset );
2364 spoke.
Append( seg.
A - offset );
2366 spoke.
Append( segL.
B + direction );
2367 spoke.
Append( seg.
B + direction );
2368 spoke.
Append( segR.
B + direction );
2371 aSpokesList.push_back( std::move( spoke ) );
2381 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
2384 thermalSpokeAngle =
pad->GetThermalSpokeAngle();
2403 position =
pad->ShapePos( aLayer );
2404 orientation =
pad->GetOrientation();
2412 position =
via->GetPosition();
2418 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
2425 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
2427 if( thermalSpokeAngle !=
ANGLE_0 )
2430 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
2431 it->Rotate( thermalSpokeAngle );
2436 buildSpokesFromOrigin( spokesBox, thermalSpokeAngle );
2439 auto spokeIter = aSpokesList.rbegin();
2441 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
2443 spokeIter->Rotate( orientation );
2444 spokeIter->Move( position );
2449 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
2450 aSpokesList[ii].GenerateBBoxCache();
2485 hole_base.
Append( corner );
2486 corner.
x += hole_size;
2487 hole_base.
Append( corner );
2488 corner.
y += hole_size;
2489 hole_base.
Append( corner );
2491 hole_base.
Append( corner );
2511 #define SMOOTH_MIN_VAL_MM 0.02
2512 #define SMOOTH_SMALL_VAL_MM 0.04
2528 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
2531 maxError = std::max( maxError * 2, smooth_value / 20 );
2533 switch( smooth_level )
2545 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
2558 bool zone_has_offset =
false;
2567 if( !zone_has_offset )
2581 int x_offset = bbox.
GetX() - ( bbox.
GetX() ) % gridsize - gridsize;
2582 int y_offset = bbox.
GetY() - ( bbox.
GetY() ) % gridsize - gridsize;
2585 for(
int xx = x_offset; xx <= bbox.
GetRight(); xx += gridsize )
2587 for(
int yy = y_offset; yy <= bbox.
GetBottom(); yy += gridsize )
2598 hole.
Move(
VECTOR2I( offset_opt.
x % gridsize, offset_opt.
y % gridsize ) );
2610 deflated_thickness = std::max( deflated_thickness, maxError * 2 );
2615 deflatedFilledPolys.
Deflate( deflated_thickness, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
2630 if( area < minimal_hole_area )
constexpr int ARC_HIGH_DEF
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
ZONE_SETTINGS & GetDefaultZoneSettings()
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.
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...
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
PROJECT * GetProject() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
const DRAWINGS & Drawings() 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 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 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....
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
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)
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(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
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 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 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).
bool m_PrototypeZoneFill
Whether Zone fill should always be solid for performance with large boards.
virtual PROJECT_LOCAL_SETTINGS & GetLocalSettings() const
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.
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)
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 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)
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.
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 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.
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
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)
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 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.
std::map< PCB_LAYER_ID, ZONE_LAYER_PROPERTIES > m_layerProperties
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)
const std::optional< VECTOR2I > & HatchingOffset(PCB_LAYER_ID aLayer) const
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
static constexpr EDA_ANGLE ANGLE_0
a few functions useful in geometry calculations.
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:
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
constexpr int mmToIU(double mm) const
A storage class for 128-bit hash value.
A struct recording the isolated and single-pad islands within a zone.
std::optional< VECTOR2I > hatching_offset
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::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_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
#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.