58 RESULTS(
int aOutline1,
int aOutline2,
int aVertex1,
int aVertex2 ) :
105 SEG::ecoord min_dist = std::numeric_limits<SEG::ecoord>::max();
108 auto check_pt = [&](
VERTEX* p )
110 VECTOR2D diff( p->x - aPt->
x, p->y - aPt->
y );
113 if( dist2 > 0 && dist2 < limit2 && dist2 < min_dist && p->isEar(
true ) )
122 while( p && p->
z <= maxZ )
130 while( p && p->
z >= minZ )
145 std::set<VERTEX*> visited;
158 if( ( visited.empty() || !visited.contains( p ) ) && ( q =
getPoint( p ) ) )
162 if( !visited.contains( q ) &&
164 p->
i, q->
i ).second )
168 visited.insert( p->
prev );
170 visited.insert( p->
next );
173 visited.insert( q->
prev );
175 visited.insert( q->
next );
199 m_brdOutlinesValid( false ),
201 m_progressReporter( nullptr ),
203 m_worstClearance( 0 )
218 wxASSERT_MSG(
m_commit, wxT(
"ZONE_FILLER must have a valid commit to call "
219 "SetProgressReporter" ) );
237 std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
238 std::map<std::pair<ZONE*, PCB_LAYER_ID>,
HASH_128> oldFillHashes;
239 std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
244 connectivity->ClearRatsnest();
252 :
_(
"Building zone fills..." ) );
265 zone->CacheBoundingBox();
269 for(
PAD*
pad : footprint->Pads() )
273 pad->BuildEffectiveShapes();
278 for(
ZONE* zone : footprint->Zones() )
279 zone->CacheBoundingBox();
282 footprint->BuildCourtyardCaches();
283 footprint->BuildNetTieCache();
288 auto findHighestPriorityZone = [&](
const BOX2I& aBBox,
const PCB_LAYER_ID aItemLayer,
290 const std::function<bool(
const ZONE* )> aTestFn ) ->
ZONE*
292 unsigned highestPriority = 0;
293 ZONE* highestPriorityZone =
nullptr;
298 if( zone->GetIsRuleArea() )
301 if( zone->GetAssignedPriority() < highestPriority )
304 if( !zone->IsOnLayer( aItemLayer ) )
308 if( zone->GetNumCorners() <= 2 )
311 if( !zone->GetBoundingBox().Intersects( aBBox ) )
314 if( !aTestFn( zone ) )
318 if( zone->GetAssignedPriority() > highestPriority || zone->GetNetCode() == aNetcode )
321 highestPriorityZone = zone;
325 return highestPriorityZone;
328 auto isInPourKeepoutArea = [&](
const BOX2I& aBBox,
const PCB_LAYER_ID aItemLayer,
333 if( !zone->GetIsRuleArea() )
336 if( !zone->HasKeepoutParametersSet() )
339 if( !zone->GetDoNotAllowCopperPour() )
342 if( !zone->IsOnLayer( aItemLayer ) )
346 if( zone->GetNumCorners() <= 2 )
349 if( !zone->GetBoundingBox().Intersects( aBBox ) )
352 if( zone->Outline()->Contains( aTestPoint ) )
366 via->ClearZoneLayerOverrides();
368 if( !
via->GetRemoveUnconnected() )
373 int testRadius =
via->GetDrillValue() / 2 + 1;
374 unsigned netcode =
via->GetNetCode();
375 LSET layers =
via->GetLayerSet() & boardCuMask;
378 auto viaTestFn = [&](
const ZONE* aZone ) ->
bool
385 if( !
via->ConditionallyFlashed( layer ) )
388 if( isInPourKeepoutArea( bbox, layer,
center ) )
394 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, viaTestFn );
408 for(
PAD*
pad : footprint->Pads() )
410 pad->ClearZoneLayerOverrides();
412 if( !
pad->GetRemoveUnconnected() )
417 unsigned netcode =
pad->GetNetCode();
418 LSET layers =
pad->GetLayerSet() & boardCuMask;
420 auto padTestFn = [&](
const ZONE* aZone ) ->
bool
427 if( !
pad->ConditionallyFlashed( layer ) )
430 if( isInPourKeepoutArea( bbox, layer,
center ) )
436 ZONE* zone = findHighestPriorityZone( bbox, layer, netcode, padTestFn );
447 for(
ZONE* zone : aZones )
450 if( zone->GetIsRuleArea() )
454 if( zone->GetNumCorners() <= 2 )
464 zone->BuildHashValue( layer );
465 oldFillHashes[ { zone, layer } ] = zone->GetHashValue( layer );
468 toFill.emplace_back( std::make_pair( zone, layer ) );
477 auto check_fill_dependency =
485 if( aOtherZone->GetFillFlag( aLayer ) )
490 if( aOtherZone->GetIsRuleArea() )
494 if( aOtherZone->GetNumCorners() <= 2 )
498 if( !aOtherZone->GetLayerSet().test( aLayer ) )
505 if( aOtherZone->SameNet( aZone ) )
513 if( !inflatedBBox.
Intersects( aOtherZone->GetBoundingBox() ) )
520 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
523 ZONE* zone = aFillItem.first;
528 for(
ZONE* otherZone : aZones )
530 if( otherZone == zone )
533 if( check_fill_dependency( zone, layer, otherZone ) )
548 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
550 if( !zoneLock.owns_lock() )
567 auto tesselate_lambda =
568 [&]( std::pair<ZONE*, PCB_LAYER_ID> aFillItem ) ->
int
574 ZONE* zone = aFillItem.first;
577 std::unique_lock<std::mutex> zoneLock( zone->
GetLock(), std::try_to_lock );
579 if( !zoneLock.owns_lock() )
591 std::vector<std::pair<std::future<int>,
int>> returns;
592 returns.reserve( toFill.size() );
594 bool cancelled =
false;
598 for(
const std::pair<ZONE*, PCB_LAYER_ID>& fillItem : toFill )
599 returns.emplace_back( std::make_pair(
tp.submit( fill_lambda, fillItem ), 0 ) );
601 while( !cancelled && finished != 2 * toFill.size() )
603 for(
size_t ii = 0; ii < returns.size(); ++ii )
605 auto& ret = returns[ii];
610 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
612 if( status == std::future_status::ready )
614 if( ret.first.get() )
623 if( ret.second == 0 )
624 returns[ii].first =
tp.submit( fill_lambda, toFill[ii] );
625 else if( ret.second == 1 )
626 returns[ii].first =
tp.submit( tesselate_lambda, toFill[ii] );
631 std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
645 for(
auto& ret : returns )
647 if( ret.first.valid() )
649 std::future_status status = ret.first.wait_for( std::chrono::seconds( 0 ) );
651 while( status != std::future_status::ready )
656 status = ret.first.wait_for( std::chrono::milliseconds( 100 ) );
675 connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
676 connectivity->SetProgressReporter(
nullptr );
681 for(
ZONE* zone : aZones )
684 if( zone->GetIsRuleArea() )
687 zone->SetIsFilled(
true );
693 for(
const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
696 bool allIslands =
true;
698 for(
const auto& [ layer, layerIslands ] : zoneIslands )
700 if( layerIslands.m_IsolatedOutlines.size()
701 !=
static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
711 for(
const auto& [ layer, layerIslands ] : zoneIslands )
716 if( layerIslands.m_IsolatedOutlines.empty() )
719 std::vector<int> islands = layerIslands.m_IsolatedOutlines;
723 std::sort( islands.begin(), islands.end(), std::greater<int>() );
725 std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
726 long long int minArea = zone->GetMinIslandArea();
729 for(
int idx : islands )
733 if( mode == ISLAND_REMOVAL_MODE::ALWAYS )
734 poly->DeletePolygonAndTriangulationData( idx,
false );
735 else if ( mode == ISLAND_REMOVAL_MODE::AREA && outline.
Area(
true ) < minArea )
736 poly->DeletePolygonAndTriangulationData( idx,
false );
738 zone->SetIsIsland( layer, idx );
741 poly->UpdateTriangulationDataHash();
742 zone->CalculateFilledArea();
751 using island_check_return = std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
int>>;
753 std::vector<std::pair<std::shared_ptr<SHAPE_POLY_SET>,
double>> polys_to_check;
758 for(
ZONE* zone : aZones )
768 double minArea = (double) zone->GetMinThickness() * zone->GetMinThickness() * 3;
775 polys_to_check.emplace_back( zone->GetFilledPolysList( layer ), minArea );
780 [&](
int aStart,
int aEnd ) -> island_check_return
782 island_check_return retval;
784 for(
int ii = aStart; ii < aEnd && !cancelled; ++ii )
786 auto [poly, minArea] = polys_to_check[ii];
788 for(
int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
793 double island_area = test_poly.
Area();
795 if( island_area < minArea )
807 if( intersection.
Area() < island_area / 2.0 )
808 retval.emplace_back( poly, jj );
815 auto island_returns =
tp.parallelize_loop( 0, polys_to_check.size(), island_lambda );
819 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
821 std::future<island_check_return>& ret = island_returns[ii];
825 std::future_status status = ret.wait_for( std::chrono::seconds( 0 ) );
827 while( status != std::future_status::ready )
837 status = ret.wait_for( std::chrono::milliseconds( 100 ) );
845 for(
size_t ii = 0; ii < island_returns.size(); ++ii )
847 std::future<island_check_return>& ret = island_returns[ii];
851 for(
auto& action_item : ret.get() )
852 action_item.first->DeletePolygonAndTriangulationData( action_item.second,
true );
856 for(
ZONE* zone : aZones )
857 zone->CalculateFilledArea();
862 bool outOfDate =
false;
864 for(
ZONE* zone : aZones )
867 if( zone->GetIsRuleArea() )
872 zone->BuildHashValue( layer );
874 if( oldFillHashes[ { zone, layer } ] != zone->GetHashValue( layer ) )
881 KIDIALOG dlg( aParent,
_(
"Zone fills are out-of-date. Refill?" ),
882 _(
"Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
916 if( aPad->
GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
924 std::vector<VECTOR2I> convex_hull;
929 for(
const VECTOR2I& pt : convex_hull )
958 switch( aItem->
Type() )
965 if(
text->IsVisible() )
967 if(
text->IsKnockout() )
1017 std::vector<PAD*>& aThermalConnectionPads,
1018 std::vector<PAD*>& aNoConnectionPads )
1024 std::shared_ptr<SHAPE> padShape;
1030 for(
PAD*
pad : footprint->Pads() )
1032 BOX2I padBBox =
pad->GetBoundingBox();
1038 bool noConnection =
pad->GetNetCode() != aZone->
GetNetCode();
1045 noConnection =
true;
1052 aNoConnectionPads.push_back(
pad );
1058 connection = ZONE_CONNECTION::FULL;
1062 constraint = bds.
m_DRCEngine->EvalZoneConnection(
pad, aZone, aLayer );
1066 if( connection == ZONE_CONNECTION::THERMAL && !
pad->CanFlashLayer( aLayer ) )
1067 connection = ZONE_CONNECTION::NONE;
1069 switch( connection )
1071 case ZONE_CONNECTION::THERMAL:
1072 padShape =
pad->GetEffectiveShape( aLayer, FLASHING::ALWAYS_FLASHED );
1074 if( aFill.
Collide( padShape.get(), 0 ) )
1080 aThermalConnectionPads.push_back(
pad );
1086 case ZONE_CONNECTION::NONE:
1095 if(
pad->FlashLayer( aLayer ) )
1099 else if(
pad->GetDrillSize().x > 0 )
1102 pad, aZone, aLayer );
1107 holeClearance = padClearance;
1130 const std::vector<PAD*>& aNoConnectionPads,
1136 auto checkForCancel =
1139 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1152 auto evalRulesForItems =
1166 auto knockoutPadClearance =
1171 bool hasHole = aPad->GetDrillSize().x > 0;
1172 bool flashLayer = aPad->FlashLayer( aLayer );
1173 bool platedHole = hasHole && aPad->GetAttribute() == PAD_ATTRIB::PTH;
1175 if( flashLayer || platedHole )
1178 aZone, aPad, aLayer ) );
1181 if( flashLayer && gap >= 0 )
1182 addKnockout( aPad, aLayer, gap + extra_margin, aHoles );
1187 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
1191 aZone, aPad, aLayer ) );
1194 aZone, aPad, aLayer ) );
1201 for(
PAD*
pad : aNoConnectionPads )
1206 knockoutPadClearance(
pad );
1211 auto knockoutTrackClearance =
1214 if( aTrack->GetBoundingBox().Intersects( zone_boundingbox ) )
1216 bool sameNet = aTrack->GetNetCode() == aZone->
GetNetCode();
1222 aZone, aTrack, aLayer );
1235 aZone, aTrack, aLayer ) );
1242 if(
via->FlashLayer( aLayer ) && gap > 0 )
1244 via->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1249 aZone,
via, aLayer ) );
1254 aZone,
via, aLayer ) );
1262 radius + gap + extra_margin,
1270 aTrack->TransformShapeToPolygon( aHoles, aLayer, gap + extra_margin,
1279 if( !track->IsOnLayer( aLayer ) )
1285 knockoutTrackClearance( track );
1290 auto knockoutGraphicClearance =
1298 bool sameNet = shapeNet == aZone->
GetNetCode();
1304 if( aItem->IsOnLayer( aLayer )
1306 || aItem->IsOnLayer(
Margin ) )
1308 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1310 bool ignoreLineWidths =
false;
1312 aZone, aItem, aLayer );
1314 if( aItem->IsOnLayer( aLayer ) && !sameNet )
1317 aZone, aItem, aLayer ) );
1319 else if( aItem->IsOnLayer(
Edge_Cuts ) )
1322 aZone, aItem, aLayer ) );
1323 ignoreLineWidths =
true;
1325 else if( aItem->IsOnLayer(
Margin ) )
1328 aZone, aItem, aLayer ) );
1333 gap += extra_margin;
1334 addKnockout( aItem, aLayer, gap, ignoreLineWidths, aHoles );
1340 auto knockoutCourtyardClearance =
1343 if( aFootprint->GetBoundingBox().Intersects( zone_boundingbox ) )
1346 aFootprint, aLayer );
1350 aHoles.
Append( aFootprint->GetCourtyard( aLayer ) );
1363 knockoutCourtyardClearance( footprint );
1364 knockoutGraphicClearance( &footprint->Reference() );
1365 knockoutGraphicClearance( &footprint->Value() );
1367 std::set<PAD*> allowedNetTiePads;
1371 if( footprint->IsNetTie() )
1373 for(
PAD*
pad : footprint->Pads() )
1382 if(
pad->IsOnLayer( aLayer ) )
1383 allowedNetTiePads.insert(
pad );
1385 for(
PAD* other : footprint->GetNetTiePads(
pad ) )
1387 if( other->IsOnLayer( aLayer ) )
1388 allowedNetTiePads.insert( other );
1394 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1399 BOX2I itemBBox = item->GetBoundingBox();
1401 if( !zone_boundingbox.
Intersects( itemBBox ) )
1404 bool skipItem =
false;
1406 if( item->IsOnLayer( aLayer ) )
1408 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape();
1410 for(
PAD*
pad : allowedNetTiePads )
1412 if(
pad->GetBoundingBox().Intersects( itemBBox )
1413 &&
pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
1422 knockoutGraphicClearance( item );
1431 knockoutGraphicClearance( item );
1436 auto knockoutZoneClearance =
1437 [&](
ZONE* aKnockout )
1440 if( !aKnockout->GetLayerSet().test( aLayer ) )
1443 if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
1445 if( aKnockout->GetIsRuleArea() )
1448 aKnockout->TransformSmoothedOutlineToPolygon( aHoles, 0,
m_maxError,
1454 aZone, aKnockout, aLayer ) );
1457 aZone, aKnockout, aLayer ) );
1460 aKnockout->TransformShapeToPolygon( poly, aLayer, gap + extra_margin,
1476 if( otherZone->GetIsRuleArea() )
1478 if( otherZone->GetDoNotAllowCopperPour() && !aZone->
IsTeardropArea() )
1479 knockoutZoneClearance( otherZone );
1481 else if( otherZone->HigherPriority( aZone ) )
1483 if( !otherZone->SameNet( aZone ) )
1484 knockoutZoneClearance( otherZone );
1490 for(
ZONE* otherZone : footprint->Zones() )
1495 if( otherZone->GetIsRuleArea() )
1497 if( otherZone->GetDoNotAllowCopperPour() && !aZone->
IsTeardropArea() )
1498 knockoutZoneClearance( otherZone );
1500 else if( otherZone->HigherPriority( aZone ) )
1502 if( !otherZone->SameNet( aZone ) )
1503 knockoutZoneClearance( otherZone );
1521 auto knockoutZoneOutline =
1522 [&](
ZONE* aKnockout )
1525 if( !aKnockout->GetLayerSet().test( aLayer ) )
1528 if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
1544 if( otherZone->SameNet( aZone )
1548 if( !otherZone->IsTeardropArea() )
1549 knockoutZoneOutline( otherZone );
1555 for(
ZONE* otherZone : footprint->Zones() )
1557 if( otherZone->SameNet( aZone ) && otherZone->HigherPriority( aZone ) )
1560 if( !otherZone->IsTeardropArea() )
1561 knockoutZoneOutline( otherZone );
1579 std::map<int, std::vector<std::pair<int, VECTOR2I>>> insertion_points;
1591 insertion_points[result.m_outline1].push_back( { result.m_vertex1, pt1 } );
1592 insertion_points[result.m_outline1].push_back( { result.m_vertex1, pt2 } );
1595 for(
auto& [outline, vertices] : insertion_points )
1603 std::stable_sort( vertices.begin(), vertices.end(),
1604 [](
const std::pair<int, VECTOR2I>& a,
const std::pair<int, VECTOR2I>& b )
1606 return a.first > b.first;
1609 for(
const auto& [vertex, pt] : vertices )
1610 line.
Insert( vertex + 1, pt );
1615#define DUMP_POLYS_TO_COPPER_LAYER( a, b, c ) \
1616 { if( m_debugZoneFiller && aDebugLayer == b ) \
1618 m_board->SetLayerName( b, c ); \
1619 SHAPE_POLY_SET d = a; \
1655 CORNER_STRATEGY fastCornerStrategy = CORNER_STRATEGY::CHAMFER_ALL_CORNERS;
1658 std::vector<PAD*> thermalConnectionPads;
1659 std::vector<PAD*> noConnectionPads;
1660 std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
1663 aFillPolys = aSmoothedOutline;
1700 static const bool USE_BBOX_CACHES =
true;
1727 const VECTOR2I& testPt = spoke.CPoint( 3 );
1730 if( testAreas.
Contains( testPt, -1, 1, USE_BBOX_CACHES ) )
1739 if( interval++ > 400 )
1752 if( &other != &spoke
1753 && other.PointInside( testPt, 1, USE_BBOX_CACHES )
1754 && spoke.PointInside( other.CPoint( 3 ), 1, USE_BBOX_CACHES ) )
1784 for(
int ii = aFillPolys.
OutlineCount() - 1; ii >= 0; ii-- )
1786 std::vector<SHAPE_LINE_CHAIN>& island = aFillPolys.
Polygon( ii );
1787 BOX2I islandExtents;
1789 for(
const VECTOR2I& pt : island.front().CPoints() )
1791 islandExtents.
Merge( pt );
1810 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1845 for(
PAD*
pad : thermalConnectionPads )
1874 auto checkForCancel =
1877 return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
1880 auto knockoutGraphicClearance =
1883 if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
1884 && aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
1887 aZone, aItem, aLayer );
1898 knockoutGraphicClearance( &footprint->Reference() );
1899 knockoutGraphicClearance( &footprint->Value() );
1901 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1902 knockoutGraphicClearance( item );
1910 knockoutGraphicClearance( item );
1913 aFillPolys = aSmoothedOutline;
1918 if( !keepout->GetIsRuleArea() )
1921 if( !keepout->HasKeepoutParametersSet() )
1924 if( keepout->GetDoNotAllowCopperPour() && keepout->IsOnLayer( aLayer ) )
1926 if( keepout->GetBoundingBox().Intersects( zone_boundingbox ) )
1928 if( keepout->Outline()->ArcCount() == 0 )
1951 if( aZone->
GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
1980 debugLayer = aLayer;
1984 if( !aZone->
BuildSmoothedPoly( maxExtents, aLayer, boardOutline, &smoothedPoly ) )
1992 if(
fillCopperZone( aZone, aLayer, debugLayer, smoothedPoly, maxExtents, aFillPolys ) )
2009 const std::vector<PAD*>& aSpokedPadsList,
2010 std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
2023 for(
PAD*
pad : aSpokedPadsList )
2026 if( !
pad->IsOnLayer( aLayer ) )
2030 int thermalReliefGap = constraint.
GetValue().
Min();
2038 int spoke_max_allowed_w = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
2040 spoke_w = std::clamp( spoke_w, constraint.
Value().
Min(), constraint.
Value().
Max() );
2043 spoke_w = std::min( spoke_w, spoke_max_allowed_w );
2046 if( spoke_w < aZone->GetMinThickness() )
2049 int spoke_half_w = spoke_w / 2;
2052 BOX2I itemBB =
pad->GetBoundingBox();
2058 bool customSpokes =
false;
2060 if(
pad->GetShape( aLayer ) == PAD_SHAPE::CUSTOM )
2062 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2064 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
2066 customSpokes =
true;
2077 auto buildSpokesFromOrigin =
2085 double dx = direction.
x;
2086 double dy = direction.y;
2090 if( direction.x == 0 )
2092 else if( direction.y == 0 )
2097 double tx = std::min( half_size.
x /
std::abs( dx ),
2099 return VECTOR2I( dx * tx, dy * tx );
2111 for(
const EDA_ANGLE& spokeAngle : angles )
2113 VECTOR2D direction( spokeAngle.Cos(), spokeAngle.Sin() );
2116 VECTOR2I intersection = intersectLineBox( direction );
2126 aSpokesList.push_back( std::move( spoke ) );
2135 pad->TransformShapeToPolygon( thermalPoly, aLayer, thermalReliefGap +
epsilon,
2139 thermalOutline = thermalPoly.
Outline( 0 );
2143 auto trimToOutline = [&](
SEG& aSegment )
2147 if( padOutline.
Intersect( aSegment, intersections ) )
2149 intersections.clear();
2152 if( thermalOutline.
Intersect( aSegment, intersections ) )
2154 aSegment.B = intersections.front().p;
2161 for(
const std::shared_ptr<PCB_SHAPE>& primitive :
pad->GetPrimitives( aLayer ) )
2163 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::SEGMENT )
2165 SEG seg( primitive->GetStart(), primitive->GetEnd() );
2170 seg.
A +=
pad->ShapePos( aLayer );
2171 seg.
B +=
pad->ShapePos( aLayer );
2186 if( trimToOutline( seg ) )
2188 VECTOR2I direction = ( seg.
B - seg.
A ).Resize( spoke_half_w );
2191 SEG segL( seg.
A - direction - offset, seg.
B + direction - offset );
2192 SEG segR( seg.
A - direction + offset, seg.
B + direction + offset );
2194 if( trimToOutline( segL ) && trimToOutline( segR ) )
2201 spoke.
Append( seg.
A + offset );
2202 spoke.
Append( seg.
A - offset );
2204 spoke.
Append( segL.
B + direction );
2205 spoke.
Append( seg.
B + direction );
2206 spoke.
Append( segR.
B + direction );
2209 aSpokesList.push_back( std::move( spoke ) );
2232 spokesBox.
Inflate( thermalReliefGap +
epsilon + zone_half_width );
2237 if(
pad->GetShape( aLayer ) == PAD_SHAPE::CIRCLE
2238 || (
pad->GetShape( aLayer ) == PAD_SHAPE::OVAL
2239 &&
pad->GetSizeX() ==
pad->GetSizeY() ) )
2241 buildSpokesFromOrigin( spokesBox,
ANGLE_0 );
2246 for(
auto it = aSpokesList.rbegin(); it != aSpokesList.rbegin() + 4; ++it )
2247 it->Rotate(
pad->GetThermalSpokeAngle() );
2252 buildSpokesFromOrigin( spokesBox,
pad->GetThermalSpokeAngle() );
2255 auto spokeIter = aSpokesList.rbegin();
2257 for(
int ii = 0; ii < 4; ++ii, ++spokeIter )
2259 spokeIter->Rotate(
pad->GetOrientation() );
2260 spokeIter->Move(
pad->ShapePos( aLayer ) );
2268 for(
size_t ii = 0; ii < aSpokesList.size(); ++ii )
2269 aSpokesList[ii].GenerateBBoxCache();
2305 hole_base.
Append( corner );
2306 corner.
x += hole_size;
2307 hole_base.
Append( corner );
2308 corner.
y += hole_size;
2309 hole_base.
Append( corner );
2311 hole_base.
Append( corner );
2331 #define SMOOTH_MIN_VAL_MM 0.02
2332 #define SMOOTH_SMALL_VAL_MM 0.04
2348 smooth_value = std::min( smooth_value, aZone->
GetHatchGap() / 2 );
2351 maxError = std::max( maxError * 2, smooth_value / 20 );
2353 switch( smooth_level )
2365 hole_base = smooth_hole.
Fillet( smooth_value, maxError ).
Outline( 0 );
2377 for(
int xx = 0; ; xx++ )
2379 int xpos = xx * gridsize;
2384 for(
int yy = 0; ; yy++ )
2386 int ypos = yy * gridsize;
2415 CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
2420 deflatedOutline.
Deflate( outline_margin, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
2432 int min_apron_radius = ( aZone->
GetHatchGap() * 10 ) / 19;
2441 &&
via->IsOnLayer( aLayer )
2442 &&
via->GetBoundingBox().Intersects( zone_boundingbox ) )
2444 int r = std::max( min_apron_radius,
2445 via->GetDrillValue() / 2 + outline_margin );
2455 for(
PAD*
pad : footprint->Pads() )
2458 &&
pad->IsOnLayer( aLayer )
2459 &&
pad->GetBoundingBox().Intersects( zone_boundingbox ) )
2466 int pad_width = std::min(
pad->GetSize( aLayer ).x,
pad->GetSize( aLayer ).y );
2467 int slot_width = std::min(
pad->GetDrillSize().x,
pad->GetDrillSize().y );
2468 int min_annular_ring_width = ( pad_width - slot_width ) / 2;
2469 int clearance = std::max( min_apron_radius - pad_width / 2,
2470 outline_margin - min_annular_ring_width );
2473 pad->TransformShapeToPolygon( aprons, aLayer,
clearance, maxError,
2489 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
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
void SetParentGroup(PCB_GROUP *aGroup)
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
virtual void SetIsKnockout(bool aKnockout)
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
const ZONES & Zones() const
int GetMaxClearanceValue() const
Returns the maximum clearance value for any object on the board.
int GetCopperLayerCount() const
const FOOTPRINTS & Footprints() const
const TRACKS & Tracks() const
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
const DRAWINGS & Drawings() const
constexpr const Vec & GetPosition() 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 size_type GetWidth() const
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
constexpr const Vec GetCenter() const
constexpr size_type GetHeight() const
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Represent a set of changes (additions, deletions or modifications) of a data model (e....
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
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 InternalCuMask()
Return a complete set of internal copper layers which is all Cu layers except F_Cu and B_Cu.
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
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 TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
void SetPosition(const VECTOR2I &aPos) override
PADSTACK::CUSTOM_SHAPE_ZONE_MODE GetCustomShapeInZoneOpt() const
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
bool TransformHoleToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Build the corner list of the polygonal drill shape in the board coordinate system.
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
A progress reporter interface for use in multi-threaded environments.
virtual bool IsCancelled() const =0
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
virtual void AdvancePhase()=0
Use the next available virtual zone of the dialog progress bar.
virtual void AdvanceProgress()=0
Increment the progress bar length (inside the current virtual zone).
virtual void SetMaxProgress(int aMaxProgress)=0
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
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.
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.
void Move(const VECTOR2I &aVector) override
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
SHAPE_POLY_SET CloneDropTriangulation() const
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 addKnockout(PAD *aPad, PCB_LAYER_ID aLayer, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad.
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...
ZONE_FILLER(BOARD *aBoard, COMMIT *aCommit)
void buildThermalSpokes(const ZONE *box, PCB_LAYER_ID aLayer, const std::vector< PAD * > &aSpokedPadsList, std::deque< SHAPE_LINE_CHAIN > &aSpokes)
Function buildThermalSpokes Constructs a list of all thermal spokes for the given zone.
void subtractHigherPriorityZones(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aRawFill)
Removes the outlines of higher-proirity zones with the same net.
SHAPE_POLY_SET m_boardOutline
bool addHatchFillTypeOnZone(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET &aFillPolys)
for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern in filled a...
void SetProgressReporter(PROGRESS_REPORTER *aReporter)
void knockoutThermalReliefs(const ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFill, std::vector< PAD * > &aThermalConnectionPads, std::vector< PAD * > &aNoConnectionPads)
Removes thermal reliefs from the shape for any pads connected to the zone.
PROGRESS_REPORTER * m_progressReporter
bool fillCopperZone(const ZONE *aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer, const SHAPE_POLY_SET &aSmoothedOutline, const SHAPE_POLY_SET &aMaxExtents, SHAPE_POLY_SET &aFillPolys)
Function fillCopperZone Add non copper areas polygons (pads and tracks with clearance) to a filled co...
void addHoleKnockout(PAD *aPad, int aGap, SHAPE_POLY_SET &aHoles)
Add a knockout for a pad's hole.
bool fillNonCopperZone(const ZONE *aZone, PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aSmoothedOutline, SHAPE_POLY_SET &aFillPolys)
bool fillSingleZone(ZONE *aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aFillPolys)
Build the filled solid areas polygons from zone outlines (stored in m_Poly) The solid areas can be mo...
bool Fill(const std::vector< ZONE * > &aZones, bool aCheck=false, wxWindow *aParent=nullptr)
Fills the given list of zones.
Handle a list of polygons defining a copper zone.
void SetNeedRefill(bool aNeedRefill)
int GetHatchBorderAlgorithm() const
std::optional< int > GetLocalClearance() const override
void CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
const BOX2I GetBoundingBox() const override
SHAPE_POLY_SET * Outline()
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
int GetMinThickness() const
bool HigherPriority(const ZONE *aOther) const
int GetHatchThickness() const
double GetHatchHoleMinArea() const
bool IsTeardropArea() const
EDA_ANGLE GetHatchOrientation() const
bool BuildSmoothedPoly(SHAPE_POLY_SET &aSmoothedPoly, PCB_LAYER_ID aLayer, SHAPE_POLY_SET *aBoardOutline, SHAPE_POLY_SET *aSmoothedPolyWithApron=nullptr) const
ZONE_FILL_MODE GetFillMode() const
double GetHatchSmoothingValue() const
int GetHatchSmoothingLevel() const
bool IsOnCopperLayer() const override
unsigned GetAssignedPriority() const
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void BuildConvexHull(std::vector< VECTOR2I > &aResult, const std::vector< VECTOR2I > &aPoly)
Calculate the convex hull of a list of points in counter-clockwise order.
CORNER_STRATEGY
define how inflate transform build inflated polygon
@ EDGE_CLEARANCE_CONSTRAINT
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
@ THERMAL_RELIEF_GAP_CONSTRAINT
@ HOLE_CLEARANCE_CONSTRAINT
@ PHYSICAL_CLEARANCE_CONSTRAINT
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.
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_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
#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.