61 RESULTS(
int aOutline1,
int aOutline2,
int aVertex1,
int aVertex2 ) :
108 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
111 auto check_pt = [&](
VERTEX* p )
113 VECTOR2D diff( p->x - aPt->
x, p->y - aPt->
y );
116 if( dist2 > 0 && dist2 < limit2 && dist2 < min_dist && p->isEar(
true ) )
125 while( p && p->
z <= maxZ )
133 while( p && p->
z >= minZ )
148 std::set<VERTEX*> visited;
161 if( ( visited.empty() || !visited.contains( p ) ) && ( q =
getPoint( p ) ) )
165 if( !visited.contains( q ) &&
167 p->
i, q->
i ).second )
171 visited.insert( p->
prev );
173 visited.insert( p->
next );
176 visited.insert( q->
prev );
178 visited.insert( q->
next );
222 wxASSERT_MSG(
m_commit, wxT(
"ZONE_FILLER must have a valid commit to call SetProgressReporter" ) );
238 std::lock_guard<KISPINLOCK> lock(
m_board->GetConnectivity()->GetLock() );
240 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
241 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
HASH_128> oldFillHashes;
242 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
244 std::shared_ptr<CONNECTIVITY_DATA> connectivity =
m_board->GetConnectivity();
251 connectivity->ClearRatsnest();
259 :
_(
"Building zone fills..." ) );
272 zone->CacheBoundingBox();
276 for(
PAD*
pad : footprint->Pads() )
280 pad->BuildEffectiveShapes();
285 for(
ZONE* zone : footprint->Zones() )
286 zone->CacheBoundingBox();
289 footprint->BuildCourtyardCaches();
290 footprint->BuildNetTieCache();
295 auto findHighestPriorityZone =
297 const std::function<bool(
const ZONE* )>& testFn ) ->
ZONE*
299 unsigned highestPriority = 0;
300 ZONE* highestPriorityZone =
nullptr;
305 if( zone->GetIsRuleArea() )
308 if( zone->GetAssignedPriority() < highestPriority )
311 if( !zone->IsOnLayer( itemLayer ) )
315 if( zone->GetNumCorners() <= 2 )
318 if( !zone->GetBoundingBox().Intersects( bbox ) )
321 if( !testFn( zone ) )
325 if( zone->GetAssignedPriority() > highestPriority
326 || zone->GetNetCode() == netcode )
328 highestPriority = zone->GetAssignedPriority();
329 highestPriorityZone = zone;
333 return highestPriorityZone;
336 auto isInPourKeepoutArea =
341 if( !zone->GetIsRuleArea() )
344 if( !zone->HasKeepoutParametersSet() )
347 if( !zone->GetDoNotAllowZoneFills() )
350 if( !zone->IsOnLayer( itemLayer ) )
354 if( zone->GetNumCorners() <= 2 )
357 if( !zone->GetBoundingBox().Intersects( bbox ) )
360 if( zone->Outline()->Contains( testPoint ) )
376 via->ClearZoneLayerOverrides();
378 if( !
via->GetRemoveUnconnected() )
383 int holeRadius =
via->GetDrillValue() / 2 + 1;
384 int netcode =
via->GetNetCode();
385 LSET layers =
via->GetLayerSet() & boardCuMask;
389 [&](
const ZONE* aZone ) ->
bool
391 return aZone->Outline()->Contains(
center, -1, holeRadius );
396 if( !
via->ConditionallyFlashed( layer ) )
399 if( isInPourKeepoutArea( bbox, layer,
center ) )
405 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
408 && (
via->Padstack().UnconnectedLayerMode()
410 || layer ==
via->Padstack().Drill().start
411 || layer ==
via->Padstack().Drill().end ) )
423 for(
PAD*
pad : footprint->Pads() )
425 pad->ClearZoneLayerOverrides();
427 if( !
pad->GetRemoveUnconnected() )
432 int netcode =
pad->GetNetCode();
433 LSET layers =
pad->GetLayerSet() & boardCuMask;
436 [&](
const ZONE* aZone ) ->
bool
438 return aZone->Outline()->Contains(
center );
443 if( !
pad->ConditionallyFlashed( layer ) )
446 if( isInPourKeepoutArea( bbox, layer,
center ) )
452 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
463 for(
ZONE* zone : aZones )
466 if( zone->GetIsRuleArea() )
470 if( zone->GetNumCorners() <= 2 )
480 zone->BuildHashValue( layer );
481 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
484 toFill.emplace_back( std::make_pair( zone, layer ) );
493 auto check_fill_dependency =
501 if( aOtherZone->GetFillFlag( aLayer ) )
506 if( aOtherZone->GetIsRuleArea() )
510 if( aOtherZone->GetNumCorners() <= 2 )
514 if( !aOtherZone->GetLayerSet().test( aLayer ) )
521 if( aOtherZone->SameNet( aZone ) )
529 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
536 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
539 ZONE* zone = aFillItem.first;
544 for(
ZONE* otherZone : aZones )
546 if( otherZone == zone )
549 if( check_fill_dependency( zone, layer, otherZone ) )
564 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
566 if( !zoneLock.owns_lock() )
583 auto tesselate_lambda =
584 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
590 ZONE* zone = aFillItem.first;
593 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
595 if( !zoneLock.owns_lock() )
607 std::vector<std::pair<std::future<int>,
int>> returns;
608 returns.reserve( toFill.size() );
610 bool cancelled =
false;
614 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
615 returns.emplace_back( std::make_pair(
tp.submit_task( [&, fillItem]() { return fill_lambda( fillItem ); } ), 0 ) );
617 while( !cancelled && finished != 2 * toFill.size() )
619 for(
size_t ii = 0; ii < returns.size(); ++ii )
621 auto& ret = returns[ii];
626 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
628 if( status == std::future_status::ready )
630 if( ret.first.get() )
639 if( ret.second == 0 )
640 returns[ii].first =
tp.submit_task( [&, idx = ii]() {
return fill_lambda( toFill[idx] ); } );
641 else if( ret.second == 1 )
642 returns[ii].first =
tp.submit_task( [&, idx = ii]() {
return tesselate_lambda( toFill[idx] ); } );
647 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
661 for(
auto& ret : returns )
663 if( ret.first.valid() )
665 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
667 while( status != std::future_status::ready )
672 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
691 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
692 connectivity->SetProgressReporter(
nullptr );
697 for(
ZONE* zone : aZones )
700 if( zone->GetIsRuleArea() )
703 zone->SetIsFilled(
true );
709 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
712 bool allIslands =
true;
714 for(
const auto& [ layer, layerIslands ] : zoneIslands )
716 if( layerIslands.m_IsolatedOutlines.size()
717 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
727 for(
const auto& [ layer, layerIslands ] : zoneIslands )
732 if( layerIslands.m_IsolatedOutlines.empty() )
735 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
739 std::sort( islands.begin(), islands.end(), std::greater<int>() );
741 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
742 long long int minArea = zone->GetMinIslandArea();
745 for(
int idx : islands )
750 poly->DeletePolygonAndTriangulationData( idx,
false );
752 poly->DeletePolygonAndTriangulationData( idx,
false );
754 zone->SetIsIsland( layer, idx );
757 poly->UpdateTriangulationDataHash();
758 zone->CalculateFilledArea();
767 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
769 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
772 polys_to_check.reserve(
m_board->GetCopperLayerCount() * aZones.size() );
774 for(
ZONE* zone : aZones )
784 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
791 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
796 [&](
int aStart,
int aEnd ) -> island_check_return
798 island_check_return retval;
800 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
802 auto [poly, minArea] = polys_to_check[ii];
804 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
809 double island_area = test_poly.
Area();
811 if( island_area < minArea )
823 if( intersection.
Area() < island_area / 2.0 )
824 retval.emplace_back( poly, jj );
831 auto island_returns =
tp.submit_blocks( 0, polys_to_check.size(), island_lambda );
835 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
837 std::future<island_check_return>& ret = island_returns[ii];
841 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
843 while( status != std::future_status::ready )
853 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
861 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
863 std::future<island_check_return>& ret = island_returns[ii];
867 for(
auto& action_item : ret.get() )
868 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
872 for(
ZONE* zone : aZones )
873 zone->CalculateFilledArea();
878 bool outOfDate =
false;
880 for(
ZONE* zone : aZones )
883 if( zone->GetIsRuleArea() )
888 zone->BuildHashValue( layer );
890 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
896 &&
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
898 KIDIALOG dlg( aParent,
_(
"Prototype zone fill enabled. Disable setting and refill?" ),
899 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
905 m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill =
false;
907 else if( !outOfDate )
915 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
916 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
959 std::vector<VECTOR2I> convex_hull;
964 for(
const VECTOR2I& pt : convex_hull )
995 minorAxis = std::min( padSize.
x, padSize.
y );
1001 minorAxis =
via->GetWidth( aLayer );
1015 switch( aItem->
Type() )
1022 if(
text->IsVisible() )
1024 if(
text->IsKnockout() )
1081 std::vector<BOARD_ITEM*>& aThermalConnectionPads,
1082 std::vector<PAD*>& aNoConnectionPads )
1088 std::shared_ptr<SHAPE> padShape;
1094 for(
PAD*
pad : footprint->Pads() )
1096 if( !
pad->IsOnLayer( aLayer ) )
1099 BOX2I padBBox =
pad->GetBoundingBox();
1105 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1112 noConnection =
true;
1119 aNoConnectionPads.push_back(
pad );
1129 aThermalConnectionPads.push_back(
pad );
1147 switch( connection )
1152 if( aFill.
Collide( padShape.get(), 0 ) )
1158 aThermalConnectionPads.push_back(
pad );
1173 if(
pad->FlashLayer( aLayer ) )
1177 else if(
pad->GetDrillSize().x > 0 )
1180 pad, aZone, aLayer );
1185 holeClearance = padClearance;
1210 if( !
via->IsOnLayer( aLayer ) )
1213 BOX2I viaBBox =
via->GetBoundingBox();
1219 bool noConnection =
via->GetNetCode() != aZone->
GetNetCode()
1220 || (
via->Padstack().UnconnectedLayerMode()
1222 && aLayer !=
via->Padstack().Drill().start
1223 && aLayer !=
via->Padstack().Drill().end );
1228 aThermalConnectionPads.push_back(
via );
1243 const std::vector<PAD*>& aNoConnectionPads,
1249 auto checkForCancel =
1252 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1265 auto evalRulesForItems =
1279 auto knockoutPadClearance =
1284 bool hasHole = aPad->GetDrillSize().x > 0;
1285 bool flashLayer = aPad->FlashLayer( aLayer );
1288 if( flashLayer || platedHole )
1291 aZone, aPad, aLayer ) );
1294 if( flashLayer && gap >= 0 )
1295 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1304 aZone, aPad, aLayer ) );
1307 aZone, aPad, aLayer ) );
1314 for(
PAD*
pad : aNoConnectionPads )
1319 knockoutPadClearance(
pad );
1324 auto knockoutTrackClearance =
1327 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
1329 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
1335 aZone, aTrack, aLayer );
1348 aZone, aTrack, aLayer ) );
1355 if(
via->FlashLayer( aLayer ) && gap > 0 )
1357 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1362 aZone,
via, aLayer ) );
1367 aZone,
via, aLayer ) );
1375 radius + gap + extra_margin,
1383 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1392 if( !track->IsOnLayer( aLayer ) )
1398 knockoutTrackClearance( track );
1403 auto knockoutGraphicClearance =
1409 shapeNet =
static_cast<PCB_SHAPE*
>( aItem )->GetNetCode();
1411 bool sameNet = shapeNet == aZone->
GetNetCode();
1417 if( aItem->IsOnLayer( aLayer )
1419 || aItem->IsOnLayer(
Margin ) )
1421 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1423 bool ignoreLineWidths =
false;
1425 aZone, aItem, aLayer );
1427 if( aItem->IsOnLayer( aLayer ) && !sameNet )
1430 aZone, aItem, aLayer ) );
1432 else if( aItem->IsOnLayer(
Edge_Cuts ) )
1435 aZone, aItem, aLayer ) );
1436 ignoreLineWidths =
true;
1438 else if( aItem->IsOnLayer(
Margin ) )
1441 aZone, aItem, aLayer ) );
1446 gap += extra_margin;
1447 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
1453 auto knockoutCourtyardClearance =
1456 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
1459 aFootprint, aLayer );
1463 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
1476 knockoutCourtyardClearance( footprint );
1477 knockoutGraphicClearance( &footprint->Reference() );
1478 knockoutGraphicClearance( &footprint->Value() );
1480 std::set<PAD*> allowedNetTiePads;
1484 if( footprint->IsNetTie() )
1486 for(
PAD*
pad : footprint->Pads() )
1495 if(
pad->IsOnLayer( aLayer ) )
1496 allowedNetTiePads.insert(
pad );
1498 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
1500 if( other->IsOnLayer( aLayer ) )
1501 allowedNetTiePads.insert( other );
1507 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1512 BOX2I itemBBox = item->GetBoundingBox();
1514 if( !zone_boundingbox.
Intersects( itemBBox ) )
1517 bool skipItem =
false;
1519 if( item->IsOnLayer( aLayer ) )
1521 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
1523 for(
PAD*
pad : allowedNetTiePads )
1525 if(
pad->GetBoundingBox().Intersects( itemBBox )
1526 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
1535 knockoutGraphicClearance( item );
1544 knockoutGraphicClearance( item );
1549 auto knockoutZoneClearance =
1550 [&](
ZONE* aKnockout )
1553 if( !aKnockout->GetLayerSet().test( aLayer ) )
1556 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
1558 if( aKnockout->GetIsRuleArea() )
1561 aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0,
m_maxError,
1567 aZone, aKnockout, aLayer ) );
1570 aZone, aKnockout, aLayer ) );
1573 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
1586 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
1593 if( otherZone->GetIsRuleArea() )
1595 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
1596 knockoutZoneClearance( otherZone );
1598 else if( otherZone->HigherPriority( aZone ) )
1600 if( !otherZone->SameNet( aZone ) )
1601 knockoutZoneClearance( otherZone );
1607 for(
ZONE* otherZone : footprint->Zones() )
1613 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
1616 if( otherZone->GetIsRuleArea() )
1618 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
1619 knockoutZoneClearance( otherZone );
1621 else if( otherZone->HigherPriority( aZone ) )
1623 if( !otherZone->SameNet( aZone ) )
1624 knockoutZoneClearance( otherZone );
1642 auto knockoutZoneOutline =
1643 [&](
ZONE* aKnockout )
1646 if( !aKnockout->GetLayerSet().test( aLayer ) )
1649 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
1665 if( otherZone->SameNet( aZone )
1669 if( !otherZone->IsTeardropArea() )
1670 knockoutZoneOutline( otherZone );
1676 for(
ZONE* otherZone : footprint->Zones() )
1678 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
1681 if( !otherZone->IsTeardropArea() )
1682 knockoutZoneOutline( otherZone );
1700 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
1712 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt1 } );
1713 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt2 } );
1716 for(
auto& [outline, vertices] : insertion_points )
1720 if( vertices.empty() )
1725 std::stable_sort( vertices.begin(), vertices.end(),
1726 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
1728 return a.first < b.first;
1731 std::vector<VECTOR2I> new_points;
1732 new_points.reserve( line.
PointCount() + vertices.size() );
1734 size_t vertex_idx = 0;
1738 new_points.push_back( line.
CPoint( i ) );
1741 while( vertex_idx < vertices.size() && vertices[vertex_idx].first == i )
1743 new_points.push_back( vertices[vertex_idx].second );
1750 for(
const auto& pt : new_points )
1756#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
1757 { if( m_debugZoneFiller && aDebugLayer == b ) \
1759 m_board->SetLayerName( b, c ); \
1760 SHAPE_POLY_SET d = a; \
1799 std::vector<BOARD_ITEM*> thermalConnectionPads;
1800 std::vector<PAD*> noConnectionPads;
1801 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
1804 aFillPolys = aSmoothedOutline;
1841 static const bool USE_BBOX_CACHES =
true;
1868 const VECTOR2I& testPt = spoke.CPoint( 3 );
1871 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
1880 if( interval++ > 400 )
1893 if( &other != &spoke
1894 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
1895 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
1925 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
1927 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
1928 BOX2I islandExtents;
1930 for(
const VECTOR2I& pt : island.front().CPoints() )
1932 islandExtents.
Merge( pt );
1953 || !
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
1988 for(
BOARD_ITEM* item : thermalConnectionPads )
2019 auto checkForCancel =
2022 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
2025 auto knockoutGraphicItem =
2028 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
2029 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2031 addKnockout( aItem, aLayer, 0,
true, clearanceHoles );
2040 knockoutGraphicItem( &footprint->Reference() );
2041 knockoutGraphicItem( &footprint->Value() );
2043 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2044 knockoutGraphicItem( item );
2052 knockoutGraphicItem( item );
2055 aFillPolys = aSmoothedOutline;
2058 auto subtractKeepout =
2059 [&](
ZONE* candidate )
2061 if( !candidate->GetIsRuleArea() )
2064 if( !candidate->HasKeepoutParametersSet() )
2067 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
2069 if( candidate->GetBoundingBox().Intersects( zone_boundingbox ) )
2071 if( candidate->Outline()->ArcCount() == 0 )
2090 subtractKeepout( keepout );
2098 for(
ZONE* keepout : footprint->Zones() )
2099 subtractKeepout( keepout );
2140 debugLayer = aLayer;
2144 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
2152 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
2169 const std::vector<BOARD_ITEM*>& aSpokedPadsList,
2170 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
2189 if( !item->IsOnLayer( aLayer ) )
2192 int thermalReliefGap = 0;
2196 bool circular =
false;
2200 pad =
static_cast<PAD*
>( item );
2224 if( thermalReliefGap < 0 )
2238 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
2240 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
2243 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
2246 if( spoke_w < aZone->GetMinThickness() )
2255 int spoke_half_w = spoke_w / 2;
2258 BOX2I itemBB = item->GetBoundingBox();
2264 bool customSpokes =
false;
2268 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2270 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
2272 customSpokes =
true;
2283 auto buildSpokesFromOrigin =
2290 auto intersectLineBox =
2293 double dx = direction.x;
2294 double dy = direction.y;
2298 if( direction.x == 0 )
2300 else if( direction.y == 0 )
2305 double tx = std::min( half_size.
x /
std::abs( dx ),
2307 return VECTOR2I( dx * tx, dy * tx );
2319 for(
const EDA_ANGLE& spokeAngle : angles )
2321 VECTOR2D direction( spokeAngle.Cos(), spokeAngle.Sin() );
2324 VECTOR2I intersection = intersectLineBox( direction );
2334 aSpokesList.push_back( std::move( spoke ) );
2343 pad->TransformShapeToPolygon( thermalPoly, aLayer, thermalReliefGap +
epsilon,
2347 thermalOutline = thermalPoly.
Outline( 0 );
2351 auto trimToOutline = [&](
SEG& aSegment )
2355 if( padOutline.
Intersect( aSegment, intersections ) )
2357 intersections.clear();
2360 if( thermalOutline.
Intersect( aSegment, intersections ) )
2362 aSegment.B = intersections.front().p;
2369 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2371 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
2373 SEG seg( primitive->GetStart(), primitive->GetEnd() );
2378 seg.
A +=
pad->ShapePos( aLayer );
2379 seg.
B +=
pad->ShapePos( aLayer );
2393 if( trimToOutline( seg ) )
2395 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
2399 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
2400 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
2403 if( trimToOutline( segL ) && trimToOutline( segR ) )
2411 spoke.
Append( seg.
A + offset );
2412 spoke.
Append( seg.
A - offset );
2414 spoke.
Append( segL.
B + direction );
2415 spoke.
Append( seg.
B + direction );
2416 spoke.
Append( segR.
B + direction );
2419 aSpokesList.push_back( std::move( spoke ) );
2432 thermalSpokeAngle =
pad->GetThermalSpokeAngle();
2451 position =
pad->ShapePos( aLayer );
2452 orientation =
pad->GetOrientation();
2460 position =
via->GetPosition();
2466 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
2473 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
2475 if( thermalSpokeAngle !=
ANGLE_0 )
2478 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
2479 it->Rotate( thermalSpokeAngle );
2484 buildSpokesFromOrigin( spokesBox, thermalSpokeAngle );
2487 auto spokeIter = aSpokesList.rbegin();
2489 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
2491 spokeIter->Rotate( orientation );
2492 spokeIter->Move( position );
2497 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
2498 aSpokesList[ii].GenerateBBoxCache();
2517 int maxError =
m_board->GetDesignSettings().m_MaxError;
2533 hole_base.
Append( corner );
2534 corner.
x += hole_size;
2535 hole_base.
Append( corner );
2536 corner.
y += hole_size;
2537 hole_base.
Append( corner );
2539 hole_base.
Append( corner );
2559 #define SMOOTH_MIN_VAL_MM 0.02
2560 #define SMOOTH_SMALL_VAL_MM 0.04
2576 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
2579 maxError = std::max( maxError * 2, smooth_value / 20 );
2581 switch( smooth_level )
2593 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
2606 bool zone_has_offset =
false;
2615 if( !zone_has_offset )
2628 int x_offset = bbox.
GetX() - ( bbox.
GetX() ) % gridsize - gridsize;
2629 int y_offset = bbox.
GetY() - ( bbox.
GetY() ) % gridsize - gridsize;
2632 for(
int xx = x_offset; xx <= bbox.
GetRight(); xx += gridsize )
2634 for(
int yy = y_offset; yy <= bbox.
GetBottom(); yy += gridsize )
2645 hole.
Move(
VECTOR2I( offset_opt.
x % gridsize, offset_opt.
y % gridsize ) );
2658 deflated_thickness = std::max( deflated_thickness, maxError * 2 );
2680 if( area < minimal_hole_area )
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.
int GetCopperLayerCount() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
constexpr int GetSizeMax() const
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr coord_type GetY() const
constexpr size_type GetWidth() const
constexpr 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....
MINOPTMAX< int > & Value()
const MINOPTMAX< int > & GetValue() const
ZONE_CONNECTION m_ZoneConnection
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
DRC_CONSTRAINT EvalZoneConnection(const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
KICAD_T Type() const
Returns the type of object.
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
bool SetOKCancelLabels(const ButtonLabel &ok, const ButtonLabel &cancel) override
LSET is a set of PCB_LAYER_IDs.
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
static const LSET & InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
void SetPosition(const VECTOR2I &aPos) override
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
bool TransformHoleToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Build the corner list of the polygonal drill shape in the board coordinate system.
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Function TransformTextToPolySet Convert the text to a polygonSet describing the actual character stro...
void SetPosition(const VECTOR2I &aPoint) override
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A 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.
void Clear()
Remove all points from the line chain.
double Area(bool aAbsolute=true) const
Return the area of this chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
std::vector< INTERSECTION > INTERSECTIONS
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 ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void DeletePolygon(int aIdx)
Delete aIdx-th polygon from the set.
double Area()
Return the area of this poly set.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET Fillet(int aRadius, int aErrorMax)
Return a filleted version of the polygon set.
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
SHAPE_POLY_SET CloneDropTriangulation() const
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBoxFromCaches() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
VERTEX * getPoint(VERTEX *aPt) const
std::set< RESULTS > GetResults() const
VERTEX_CONNECTOR(const BOX2I &aBBox, const SHAPE_POLY_SET &aPolys, int aDist)
std::set< RESULTS > m_results
std::deque< VERTEX > m_vertices
VERTEX * createList(const SHAPE_LINE_CHAIN &points, VERTEX *aTail=nullptr, void *aUserData=nullptr)
Create a list of vertices from a line chain.
void SetBoundingBox(const BOX2I &aBBox)
VERTEX_SET(int aSimplificationLevel)
uint32_t zOrder(const double aX, const double aY) const
Note that while the inputs are doubles, these are scaled by the size of the bounding box to fit into ...
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
void * GetUserData() const
bool isEar(bool aMatchUserData=false) const
Check whether the given vertex is in the middle of an ear.
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.
ZONE_SETTINGS handles zones parameters.
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
@ 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
a few functions useful in geometry calculations.
bool m_DebugZoneFiller
A mode that dumps the various stages of a F_Cu fill into In1_Cu through In9_Cu.
@ 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.
BARCODE class definition.
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
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::thread_pool< 0 > thread_pool
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D
#define SMOOTH_MIN_VAL_MM
int getHatchFillThermalClearance(const ZONE *aZone, BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
#define DUMP_POLYS_TO_COPPER_LAYER(a, b, c)
#define SMOOTH_SMALL_VAL_MM
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
ZONE_CONNECTION
How pads are covered by copper in zone.
@ THERMAL
Use thermal relief for pads.
@ NONE
Pads are not covered.
@ FULL
pads are covered by copper