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 ) )
377 via->ClearZoneLayerOverrides();
379 if( !
via->GetRemoveUnconnected() )
384 int holeRadius =
via->GetDrillValue() / 2 + 1;
385 int netcode =
via->GetNetCode();
386 LSET layers =
via->GetLayerSet() & boardCuMask;
390 [&](
const ZONE* aZone ) ->
bool
392 return aZone->Outline()->Contains(
center, -1, holeRadius );
397 if( !
via->ConditionallyFlashed( layer ) )
400 if( isInPourKeepoutArea( bbox, layer,
center ) )
406 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
411 || layer == padstack.
Drill().
end ) )
427 for(
PAD*
pad : footprint->Pads() )
429 pad->ClearZoneLayerOverrides();
431 if( !
pad->GetRemoveUnconnected() )
436 int netcode =
pad->GetNetCode();
437 LSET layers =
pad->GetLayerSet() & boardCuMask;
440 [&](
const ZONE* aZone ) ->
bool
442 return aZone->Outline()->Contains(
center );
447 if( !
pad->ConditionallyFlashed( layer ) )
450 if( isInPourKeepoutArea( bbox, layer,
center ) )
456 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
467 for(
ZONE* zone : aZones )
470 if( zone->GetIsRuleArea() )
474 if( zone->GetNumCorners() <= 2 )
484 zone->BuildHashValue( layer );
485 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
488 toFill.emplace_back( std::make_pair( zone, layer ) );
497 auto check_fill_dependency =
505 if( aOtherZone->GetFillFlag( aLayer ) )
510 if( aOtherZone->GetIsRuleArea() )
514 if( aOtherZone->GetNumCorners() <= 2 )
518 if( !aOtherZone->GetLayerSet().test( aLayer ) )
525 if( aOtherZone->SameNet( aZone ) )
533 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
540 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
543 ZONE* zone = aFillItem.first;
548 for(
ZONE* otherZone : aZones )
550 if( otherZone == zone )
553 if( check_fill_dependency( zone, layer, otherZone ) )
568 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
570 if( !zoneLock.owns_lock() )
587 auto tesselate_lambda =
588 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
594 ZONE* zone = aFillItem.first;
597 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
599 if( !zoneLock.owns_lock() )
611 std::vector<std::pair<std::future<int>,
int>> returns;
612 returns.reserve( toFill.size() );
614 bool cancelled =
false;
618 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
619 returns.emplace_back( std::make_pair(
tp.submit_task( [&, fillItem]() { return fill_lambda( fillItem ); } ), 0 ) );
621 while( !cancelled && finished != 2 * toFill.size() )
623 for(
size_t ii = 0; ii < returns.size(); ++ii )
625 auto& ret = returns[ii];
630 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
632 if( status == std::future_status::ready )
634 if( ret.first.get() )
643 if( ret.second == 0 )
644 returns[ii].first =
tp.submit_task( [&, idx = ii]() {
return fill_lambda( toFill[idx] ); } );
645 else if( ret.second == 1 )
646 returns[ii].first =
tp.submit_task( [&, idx = ii]() {
return tesselate_lambda( toFill[idx] ); } );
651 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
665 for(
auto& ret : returns )
667 if( ret.first.valid() )
669 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
671 while( status != std::future_status::ready )
676 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
695 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
696 connectivity->SetProgressReporter(
nullptr );
701 for(
ZONE* zone : aZones )
704 if( zone->GetIsRuleArea() )
707 zone->SetIsFilled(
true );
713 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
716 bool allIslands =
true;
718 for(
const auto& [ layer, layerIslands ] : zoneIslands )
720 if( layerIslands.m_IsolatedOutlines.size()
721 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
731 for(
const auto& [ layer, layerIslands ] : zoneIslands )
736 if( layerIslands.m_IsolatedOutlines.empty() )
739 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
743 std::sort( islands.begin(), islands.end(), std::greater<int>() );
745 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
746 long long int minArea = zone->GetMinIslandArea();
749 for(
int idx : islands )
754 poly->DeletePolygonAndTriangulationData( idx,
false );
756 poly->DeletePolygonAndTriangulationData( idx,
false );
758 zone->SetIsIsland( layer, idx );
761 poly->UpdateTriangulationDataHash();
762 zone->CalculateFilledArea();
771 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
773 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
776 polys_to_check.reserve(
m_board->GetCopperLayerCount() * aZones.size() );
778 for(
ZONE* zone : aZones )
788 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
795 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
800 [&](
int aStart,
int aEnd ) -> island_check_return
802 island_check_return retval;
804 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
806 auto [poly, minArea] = polys_to_check[ii];
808 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
813 double island_area = test_poly.
Area();
815 if( island_area < minArea )
827 if( intersection.
Area() < island_area / 2.0 )
828 retval.emplace_back( poly, jj );
835 auto island_returns =
tp.submit_blocks( 0, polys_to_check.size(), island_lambda );
839 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
841 std::future<island_check_return>& ret = island_returns[ii];
845 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
847 while( status != std::future_status::ready )
857 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
865 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
867 std::future<island_check_return>& ret = island_returns[ii];
871 for(
auto& action_item : ret.get() )
872 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
876 for(
ZONE* zone : aZones )
877 zone->CalculateFilledArea();
882 bool outOfDate =
false;
884 for(
ZONE* zone : aZones )
887 if( zone->GetIsRuleArea() )
892 zone->BuildHashValue( layer );
894 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
900 &&
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
902 KIDIALOG dlg( aParent,
_(
"Prototype zone fill enabled. Disable setting and refill?" ),
903 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
909 m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill =
false;
911 else if( !outOfDate )
919 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
920 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
963 std::vector<VECTOR2I> convex_hull;
968 for(
const VECTOR2I& pt : convex_hull )
999 minorAxis = std::min( padSize.
x, padSize.
y );
1005 minorAxis =
via->GetWidth( aLayer );
1019 switch( aItem->
Type() )
1026 if(
text->IsVisible() )
1028 if(
text->IsKnockout() )
1085 std::vector<BOARD_ITEM*>& aThermalConnectionPads,
1086 std::vector<PAD*>& aNoConnectionPads )
1092 std::shared_ptr<SHAPE> padShape;
1098 for(
PAD*
pad : footprint->Pads() )
1100 if( !
pad->IsOnLayer( aLayer ) )
1103 BOX2I padBBox =
pad->GetBoundingBox();
1109 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1116 noConnection =
true;
1123 aNoConnectionPads.push_back(
pad );
1133 aThermalConnectionPads.push_back(
pad );
1151 switch( connection )
1156 if( aFill.
Collide( padShape.get(), 0 ) )
1162 aThermalConnectionPads.push_back(
pad );
1177 if(
pad->FlashLayer( aLayer ) )
1181 else if(
pad->GetDrillSize().x > 0 )
1184 pad, aZone, aLayer );
1189 holeClearance = padClearance;
1214 if( !
via->IsOnLayer( aLayer ) )
1217 BOX2I viaBBox =
via->GetBoundingBox();
1223 bool noConnection =
via->GetNetCode() != aZone->
GetNetCode()
1224 || (
via->Padstack().UnconnectedLayerMode()
1226 && aLayer !=
via->Padstack().Drill().start
1227 && aLayer !=
via->Padstack().Drill().end );
1232 aThermalConnectionPads.push_back(
via );
1247 const std::vector<PAD*>& aNoConnectionPads,
1253 auto checkForCancel =
1256 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1269 auto evalRulesForItems =
1283 auto knockoutPadClearance =
1288 bool hasHole = aPad->GetDrillSize().x > 0;
1289 bool flashLayer = aPad->FlashLayer( aLayer );
1292 if( flashLayer || platedHole )
1295 aZone, aPad, aLayer ) );
1298 if( flashLayer && gap >= 0 )
1299 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1308 aZone, aPad, aLayer ) );
1311 aZone, aPad, aLayer ) );
1318 for(
PAD*
pad : aNoConnectionPads )
1323 knockoutPadClearance(
pad );
1328 auto knockoutTrackClearance =
1331 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
1333 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
1339 aZone, aTrack, aLayer );
1352 aZone, aTrack, aLayer ) );
1359 if(
via->FlashLayer( aLayer ) && gap > 0 )
1361 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1366 aZone,
via, aLayer ) );
1371 aZone,
via, aLayer ) );
1379 radius + gap + extra_margin,
1387 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1396 if( !track->IsOnLayer( aLayer ) )
1402 knockoutTrackClearance( track );
1407 auto knockoutGraphicClearance =
1413 shapeNet =
static_cast<PCB_SHAPE*
>( aItem )->GetNetCode();
1415 bool sameNet = shapeNet == aZone->
GetNetCode();
1421 if( aItem->IsOnLayer( aLayer )
1423 || aItem->IsOnLayer(
Margin ) )
1425 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1427 bool ignoreLineWidths =
false;
1429 aZone, aItem, aLayer );
1431 if( aItem->IsOnLayer( aLayer ) && !sameNet )
1434 aZone, aItem, aLayer ) );
1436 else if( aItem->IsOnLayer(
Edge_Cuts ) )
1439 aZone, aItem, aLayer ) );
1440 ignoreLineWidths =
true;
1442 else if( aItem->IsOnLayer(
Margin ) )
1445 aZone, aItem, aLayer ) );
1450 gap += extra_margin;
1451 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
1457 auto knockoutCourtyardClearance =
1460 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
1463 aFootprint, aLayer );
1467 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
1480 knockoutCourtyardClearance( footprint );
1481 knockoutGraphicClearance( &footprint->Reference() );
1482 knockoutGraphicClearance( &footprint->Value() );
1484 std::set<PAD*> allowedNetTiePads;
1488 if( footprint->IsNetTie() )
1490 for(
PAD*
pad : footprint->Pads() )
1499 if(
pad->IsOnLayer( aLayer ) )
1500 allowedNetTiePads.insert(
pad );
1502 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
1504 if( other->IsOnLayer( aLayer ) )
1505 allowedNetTiePads.insert( other );
1511 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1516 BOX2I itemBBox = item->GetBoundingBox();
1518 if( !zone_boundingbox.
Intersects( itemBBox ) )
1521 bool skipItem =
false;
1523 if( item->IsOnLayer( aLayer ) )
1525 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
1527 for(
PAD*
pad : allowedNetTiePads )
1529 if(
pad->GetBoundingBox().Intersects( itemBBox )
1530 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
1539 knockoutGraphicClearance( item );
1548 knockoutGraphicClearance( item );
1553 auto knockoutZoneClearance =
1554 [&](
ZONE* aKnockout )
1557 if( !aKnockout->GetLayerSet().test( aLayer ) )
1560 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
1562 if( aKnockout->GetIsRuleArea() )
1565 aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0,
m_maxError,
1571 aZone, aKnockout, aLayer ) );
1574 aZone, aKnockout, aLayer ) );
1577 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
1590 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
1597 if( otherZone->GetIsRuleArea() )
1599 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
1600 knockoutZoneClearance( otherZone );
1602 else if( otherZone->HigherPriority( aZone ) )
1604 if( !otherZone->SameNet( aZone ) )
1605 knockoutZoneClearance( otherZone );
1611 for(
ZONE* otherZone : footprint->Zones() )
1617 if( !otherZone->GetBoundingBox().Intersects( zone_boundingbox ) )
1620 if( otherZone->GetIsRuleArea() )
1622 if( otherZone->GetDoNotAllowZoneFills() && !aZone->
IsTeardropArea() )
1623 knockoutZoneClearance( otherZone );
1625 else if( otherZone->HigherPriority( aZone ) )
1627 if( !otherZone->SameNet( aZone ) )
1628 knockoutZoneClearance( otherZone );
1646 auto knockoutZoneOutline =
1647 [&](
ZONE* aKnockout )
1650 if( !aKnockout->GetLayerSet().test( aLayer ) )
1653 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
1669 if( otherZone->SameNet( aZone )
1673 if( !otherZone->IsTeardropArea() )
1674 knockoutZoneOutline( otherZone );
1680 for(
ZONE* otherZone : footprint->Zones() )
1682 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
1685 if( !otherZone->IsTeardropArea() )
1686 knockoutZoneOutline( otherZone );
1704 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
1716 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt1 } );
1717 insertion_points[
result.m_outline1].push_back( {
result.m_vertex1, pt2 } );
1720 for(
auto& [outline, vertices] : insertion_points )
1724 if( vertices.empty() )
1729 std::stable_sort( vertices.begin(), vertices.end(),
1730 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
1732 return a.first < b.first;
1735 std::vector<VECTOR2I> new_points;
1736 new_points.reserve( line.
PointCount() + vertices.size() );
1738 size_t vertex_idx = 0;
1742 new_points.push_back( line.
CPoint( i ) );
1745 while( vertex_idx < vertices.size() && vertices[vertex_idx].first == i )
1747 new_points.push_back( vertices[vertex_idx].second );
1754 for(
const auto& pt : new_points )
1760#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
1761 { if( m_debugZoneFiller && aDebugLayer == b ) \
1763 m_board->SetLayerName( b, c ); \
1764 SHAPE_POLY_SET d = a; \
1803 std::vector<BOARD_ITEM*> thermalConnectionPads;
1804 std::vector<PAD*> noConnectionPads;
1805 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
1808 aFillPolys = aSmoothedOutline;
1845 static const bool USE_BBOX_CACHES =
true;
1872 const VECTOR2I& testPt = spoke.CPoint( 3 );
1875 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
1884 if( interval++ > 400 )
1897 if( &other != &spoke
1898 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
1899 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
1929 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
1931 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
1932 BOX2I islandExtents;
1934 for(
const VECTOR2I& pt : island.front().CPoints() )
1936 islandExtents.
Merge( pt );
1957 || !
m_board->GetProject()->GetLocalSettings().m_PrototypeZoneFill ) )
1992 for(
BOARD_ITEM* item : thermalConnectionPads )
2023 auto checkForCancel =
2026 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
2029 auto knockoutGraphicItem =
2032 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
2033 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
2035 addKnockout( aItem, aLayer, 0,
true, clearanceHoles );
2044 knockoutGraphicItem( &footprint->Reference() );
2045 knockoutGraphicItem( &footprint->Value() );
2047 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
2048 knockoutGraphicItem( item );
2056 knockoutGraphicItem( item );
2059 aFillPolys = aSmoothedOutline;
2062 auto subtractKeepout =
2063 [&](
ZONE* candidate )
2065 if( !candidate->GetIsRuleArea() )
2068 if( !candidate->HasKeepoutParametersSet() )
2071 if( candidate->GetDoNotAllowZoneFills() && candidate->IsOnLayer( aLayer ) )
2073 if( candidate->GetBoundingBox().Intersects( zone_boundingbox ) )
2075 if( candidate->Outline()->ArcCount() == 0 )
2094 subtractKeepout( keepout );
2102 for(
ZONE* keepout : footprint->Zones() )
2103 subtractKeepout( keepout );
2144 debugLayer = aLayer;
2148 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
2156 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
2173 const std::vector<BOARD_ITEM*>& aSpokedPadsList,
2174 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
2193 if( !item->IsOnLayer( aLayer ) )
2196 int thermalReliefGap = 0;
2200 bool circular =
false;
2204 pad =
static_cast<PAD*
>( item );
2228 if( thermalReliefGap < 0 )
2242 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
2244 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
2247 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
2250 if( spoke_w < aZone->GetMinThickness() )
2259 int spoke_half_w = spoke_w / 2;
2262 BOX2I itemBB = item->GetBoundingBox();
2268 bool customSpokes =
false;
2272 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2274 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
2276 customSpokes =
true;
2287 auto buildSpokesFromOrigin =
2294 auto intersectLineBox =
2297 double dx = direction.x;
2298 double dy = direction.y;
2302 if( direction.x == 0 )
2304 else if( direction.y == 0 )
2309 double tx = std::min( half_size.
x /
std::abs( dx ),
2311 return VECTOR2I( dx * tx, dy * tx );
2323 for(
const EDA_ANGLE& spokeAngle : angles )
2325 VECTOR2D direction( spokeAngle.Cos(), spokeAngle.Sin() );
2328 VECTOR2I intersection = intersectLineBox( direction );
2338 aSpokesList.push_back( std::move( spoke ) );
2347 pad->TransformShapeToPolygon( thermalPoly, aLayer, thermalReliefGap +
epsilon,
2351 thermalOutline = thermalPoly.
Outline( 0 );
2355 auto trimToOutline = [&](
SEG& aSegment )
2359 if( padOutline.
Intersect( aSegment, intersections ) )
2361 intersections.clear();
2364 if( thermalOutline.
Intersect( aSegment, intersections ) )
2366 aSegment.B = intersections.front().p;
2373 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2375 if( primitive->IsProxyItem() && primitive->GetShape() ==
SHAPE_T::SEGMENT )
2377 SEG seg( primitive->GetStart(), primitive->GetEnd() );
2382 seg.
A +=
pad->ShapePos( aLayer );
2383 seg.
B +=
pad->ShapePos( aLayer );
2397 if( trimToOutline( seg ) )
2399 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
2403 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
2404 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
2407 if( trimToOutline( segL ) && trimToOutline( segR ) )
2415 spoke.
Append( seg.
A + offset );
2416 spoke.
Append( seg.
A - offset );
2418 spoke.
Append( segL.
B + direction );
2419 spoke.
Append( seg.
B + direction );
2420 spoke.
Append( segR.
B + direction );
2423 aSpokesList.push_back( std::move( spoke ) );
2436 thermalSpokeAngle =
pad->GetThermalSpokeAngle();
2455 position =
pad->ShapePos( aLayer );
2456 orientation =
pad->GetOrientation();
2464 position =
via->GetPosition();
2470 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
2477 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
2479 if( thermalSpokeAngle !=
ANGLE_0 )
2482 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
2483 it->Rotate( thermalSpokeAngle );
2488 buildSpokesFromOrigin( spokesBox, thermalSpokeAngle );
2491 auto spokeIter = aSpokesList.rbegin();
2493 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
2495 spokeIter->Rotate( orientation );
2496 spokeIter->Move( position );
2501 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
2502 aSpokesList[ii].GenerateBBoxCache();
2521 int maxError =
m_board->GetDesignSettings().m_MaxError;
2537 hole_base.
Append( corner );
2538 corner.
x += hole_size;
2539 hole_base.
Append( corner );
2540 corner.
y += hole_size;
2541 hole_base.
Append( corner );
2543 hole_base.
Append( corner );
2563 #define SMOOTH_MIN_VAL_MM 0.02
2564 #define SMOOTH_SMALL_VAL_MM 0.04
2580 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
2583 maxError = std::max( maxError * 2, smooth_value / 20 );
2585 switch( smooth_level )
2597 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
2609 auto& defaultOffsets =
m_board->GetDesignSettings().m_ZoneLayerProperties;
2612 VECTOR2I offset = defaultOffsets[aLayer].hatching_offset.value_or(
VECTOR2I() );
2614 if( localOffsets.contains( aLayer ) && localOffsets.at( aLayer ).hatching_offset.has_value() )
2615 offset = localOffsets.at( aLayer ).hatching_offset.value();
2617 int x_offset = bbox.
GetX() - ( bbox.
GetX() ) % gridsize - gridsize;
2618 int y_offset = bbox.
GetY() - ( bbox.
GetY() ) % gridsize - gridsize;
2621 for(
int xx = x_offset; xx <= bbox.
GetRight(); xx += gridsize )
2623 for(
int yy = y_offset; yy <= bbox.
GetBottom(); yy += gridsize )
2634 hole.
Move(
VECTOR2I( offset.
x % gridsize, offset.
y % gridsize ) );
2647 deflated_thickness = std::max( deflated_thickness, maxError * 2 );
2669 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.
A PADSTACK defines the characteristics of a single or multi-layer pad, in the IPC sense of the word.
UNCONNECTED_LAYER_MODE UnconnectedLayerMode() const
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
void SetPosition(const VECTOR2I &aPos) override
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
bool TransformHoleToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Build the corner list of the polygonal drill shape in the board coordinate system.
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Function TransformTextToPolySet Convert the text to a polygonSet describing the actual character stro...
void SetPosition(const VECTOR2I &aPoint) override
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A 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.
Handle a list of polygons defining a copper zone.
void SetNeedRefill(bool aNeedRefill)
std::optional< int > GetLocalClearance() const override
ZONE_LAYER_PROPERTIES & LayerProperties(PCB_LAYER_ID aLayer)
void CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
const BOX2I GetBoundingBox() const override
SHAPE_POLY_SET * Outline()
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
int GetMinThickness() const
bool HigherPriority(const ZONE *aOther) const
int GetHatchThickness() const
double GetHatchHoleMinArea() const
bool IsTeardropArea() const
EDA_ANGLE GetHatchOrientation() const
bool BuildSmoothedPoly(SHAPE_POLY_SET &aSmoothedPoly, PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aBoardOutline, SHAPE_POLY_SET *aSmoothedPolyWithApron=nullptr) const
ZONE_FILL_MODE GetFillMode() const
double GetHatchSmoothingValue() const
int GetHatchSmoothingLevel() const
bool IsOnCopperLayer() const override
unsigned GetAssignedPriority() const
void 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.
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::priority_thread_pool thread_pool
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
VECTOR2< int32_t > VECTOR2I
VECTOR2< double > VECTOR2D
#define SMOOTH_MIN_VAL_MM
int getHatchFillThermalClearance(const ZONE *aZone, BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
#define DUMP_POLYS_TO_COPPER_LAYER(a, b, c)
#define SMOOTH_SMALL_VAL_MM
ISLAND_REMOVAL_MODE
Whether or not to remove isolated islands from a zone.
ZONE_CONNECTION
How pads are covered by copper in zone.
@ THERMAL
Use thermal relief for pads.
@ NONE
Pads are not covered.
@ FULL
pads are covered by copper