Support text knockouts in non-copper zones.

This commit is contained in:
Jeff Young 2022-03-08 18:06:05 +00:00
parent 40fd8860fe
commit f6dac9eb13
2 changed files with 113 additions and 56 deletions

View File

@ -960,7 +960,7 @@ void ZONE_FILLER::subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID a
SHAPE_POLY_SET d = a; \
d.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); \
d.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); \
aRawPolys = d; \
aFillPolys = d; \
return false; \
} \
}
@ -977,11 +977,9 @@ void ZONE_FILLER::subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID a
* 5 - Removes unconnected copper islands, deleting any affected spokes
* 6 - Adds in the remaining spokes
*/
bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer,
bool ZONE_FILLER::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& aRawPolys )
const SHAPE_POLY_SET& aMaxExtents, SHAPE_POLY_SET& aFillPolys )
{
m_maxError = m_board->GetDesignSettings().m_MaxError;
@ -1010,14 +1008,14 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
std::deque<SHAPE_LINE_CHAIN> thermalSpokes;
SHAPE_POLY_SET clearanceHoles;
aRawPolys = aSmoothedOutline;
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In1_Cu, wxT( "smoothed-outline" ) );
aFillPolys = aSmoothedOutline;
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In1_Cu, wxT( "smoothed-outline" ) );
if( m_progressReporter && m_progressReporter->IsCancelled() )
return false;
knockoutThermalReliefs( aZone, aLayer, aRawPolys, thermalConnectionPads, noConnectionPads );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In2_Cu, wxT( "minus-thermal-reliefs" ) );
knockoutThermalReliefs( aZone, aLayer, aFillPolys, thermalConnectionPads, noConnectionPads );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In2_Cu, wxT( "minus-thermal-reliefs" ) );
if( m_progressReporter && m_progressReporter->IsCancelled() )
return false;
@ -1036,7 +1034,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
// Create a temporary zone that we can hit-test spoke-ends against. It's only temporary
// because the "real" subtract-clearance-holes has to be done after the spokes are added.
static const bool USE_BBOX_CACHES = true;
SHAPE_POLY_SET testAreas = aRawPolys;
SHAPE_POLY_SET testAreas = aFillPolys;
testAreas.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( testAreas, In4_Cu, wxT( "minus-clearance-holes" ) );
@ -1070,7 +1068,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
if( m_debugZoneFiller )
debugSpokes.AddOutline( spoke );
aRawPolys.AddOutline( spoke );
aFillPolys.AddOutline( spoke );
continue;
}
@ -1090,7 +1088,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
if( m_debugZoneFiller )
debugSpokes.AddOutline( spoke );
aRawPolys.AddOutline( spoke );
aFillPolys.AddOutline( spoke );
break;
}
}
@ -1101,14 +1099,14 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
if( m_progressReporter && m_progressReporter->IsCancelled() )
return false;
aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In8_Cu, wxT( "after-spoke-trimming" ) );
aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In8_Cu, wxT( "after-spoke-trimming" ) );
// Prune features that don't meet minimum-width criteria
if( half_min_width - epsilon > epsilon )
aRawPolys.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
aFillPolys.Deflate( half_min_width - epsilon, numSegs, cornerStrategy );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In9_Cu, wxT( "deflated" ) );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In9_Cu, wxT( "deflated" ) );
if( m_progressReporter && m_progressReporter->IsCancelled() )
return false;
@ -1116,7 +1114,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
// Now remove the non filled areas due to the hatch pattern
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
{
if( !addHatchFillTypeOnZone( aZone, aLayer, aDebugLayer, aRawPolys ) )
if( !addHatchFillTypeOnZone( aZone, aLayer, aDebugLayer, aFillPolys ) )
return false;
}
@ -1125,22 +1123,98 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone,
// Re-inflate after pruning of areas that don't meet minimum-width criteria
if( half_min_width - epsilon > epsilon )
aRawPolys.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
aFillPolys.Inflate( half_min_width - epsilon, numSegs, cornerStrategy );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In15_Cu, wxT( "after-reinflating" ) );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In15_Cu, wxT( "after-reinflating" ) );
// Ensure additive changes (thermal stubs and particularly inflating acute corners) do not
// add copper outside the zone boundary or inside the clearance holes
aRawPolys.BooleanIntersection( aMaxExtents, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In16_Cu, wxT( "after-trim-to-outline" ) );
aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In17_Cu, wxT( "after-trim-to-clearance-holes" ) );
aFillPolys.BooleanIntersection( aMaxExtents, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In16_Cu, wxT( "after-trim-to-outline" ) );
aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In17_Cu, wxT( "after-trim-to-clearance-holes" ) );
// Lastly give any same-net but higher-priority zones control over their own area.
subtractHigherPriorityZones( aZone, aLayer, aRawPolys );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In18_Cu, wxT( "minus-higher-priority-zones" ) );
subtractHigherPriorityZones( aZone, aLayer, aFillPolys );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In18_Cu, wxT( "minus-higher-priority-zones" ) );
aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
aFillPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
return true;
}
bool ZONE_FILLER::fillNonCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer,
const SHAPE_POLY_SET& aSmoothedOutline,
SHAPE_POLY_SET& aFillPolys )
{
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
EDA_RECT zone_boundingbox = aZone->GetCachedBoundingBox();
SHAPE_POLY_SET clearanceHoles;
long ticker = 0;
auto checkForCancel =
[&ticker]( PROGRESS_REPORTER* aReporter ) -> bool
{
return aReporter && ( ticker++ % 50 ) == 0 && aReporter->IsCancelled();
};
auto knockoutGraphicClearance =
[&]( BOARD_ITEM* aItem )
{
if( aItem->IsKnockout() && aItem->IsOnLayer( aLayer )
&& aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
{
DRC_CONSTRAINT cc = bds.m_DRCEngine->EvalRules( MECHANICAL_CLEARANCE_CONSTRAINT,
aZone, aItem, aLayer );
addKnockout( aItem, aLayer, cc.GetValue().Min(), false, clearanceHoles );
}
};
for( FOOTPRINT* footprint : m_board->Footprints() )
{
if( checkForCancel( m_progressReporter ) )
return false;
knockoutGraphicClearance( &footprint->Reference() );
knockoutGraphicClearance( &footprint->Value() );
for( BOARD_ITEM* item : footprint->GraphicalItems() )
knockoutGraphicClearance( item );
}
for( BOARD_ITEM* item : m_board->Drawings() )
{
if( checkForCancel( m_progressReporter ) )
return false;
knockoutGraphicClearance( item );
}
aFillPolys = aSmoothedOutline;
aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
// Features which are min_width should survive pruning; features that are *less* than
// min_width should not. Therefore we subtract epsilon from the min_width when
// deflating/inflating.
int half_min_width = aZone->GetMinThickness() / 2;
int epsilon = Millimeter2iu( 0.001 );
int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, FULL_CIRCLE );
aFillPolys.Deflate( half_min_width - epsilon, numSegs );
// Remove the non filled areas due to the hatch pattern
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
{
if( !addHatchFillTypeOnZone( aZone, aLayer, aLayer, aFillPolys ) )
return false;
}
// Re-inflate after pruning of areas that don't meet minimum-width criteria
if( half_min_width - epsilon > epsilon )
aFillPolys.Inflate( half_min_width - epsilon, numSegs );
aFillPolys.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
return true;
}
@ -1176,26 +1250,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_S
}
else
{
// Features which are min_width should survive pruning; features that are *less* than
// min_width should not. Therefore we subtract epsilon from the min_width when
// deflating/inflating.
int half_min_width = aZone->GetMinThickness() / 2;
int epsilon = Millimeter2iu( 0.001 );
int numSegs = GetArcToSegmentCount( half_min_width, m_maxError, FULL_CIRCLE );
smoothedPoly.Deflate( half_min_width - epsilon, numSegs );
// Remove the non filled areas due to the hatch pattern
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
addHatchFillTypeOnZone( aZone, aLayer, debugLayer, smoothedPoly );
// Re-inflate after pruning of areas that don't meet minimum-width criteria
if( half_min_width - epsilon > epsilon )
smoothedPoly.Inflate( half_min_width - epsilon, numSegs );
aFillPolys = smoothedPoly;
aFillPolys.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
if( fillNonCopperZone( aZone, aLayer, smoothedPoly, aFillPolys ) )
aZone->SetNeedRefill( false );
}
@ -1324,7 +1379,7 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer,
bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer,
PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET& aRawPolys )
PCB_LAYER_ID aDebugLayer, SHAPE_POLY_SET& aFillPolys )
{
// Build grid:
@ -1340,7 +1395,7 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer
int linethickness = thickness - aZone->GetMinThickness();
int gridsize = thickness + aZone->GetHatchGap();
SHAPE_POLY_SET filledPolys = aRawPolys;
SHAPE_POLY_SET filledPolys = aFillPolys;
// Use a area that contains the rotated bbox by orientation, and after rotate the result
// by -orientation.
if( !aZone->GetHatchOrientation().IsZero() )
@ -1462,7 +1517,7 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer
// The fill has already been deflated to ensure GetMinThickness() so we just have to
// account for anything beyond that.
SHAPE_POLY_SET deflatedFilledPolys = aRawPolys;
SHAPE_POLY_SET deflatedFilledPolys = aFillPolys;
deflatedFilledPolys.Deflate( outline_margin - aZone->GetMinThickness(), 16 );
holes.BooleanIntersection( deflatedFilledPolys, SHAPE_POLY_SET::PM_FAST );
DUMP_POLYS_TO_COPPER_LAYER( holes, In11_Cu, wxT( "fill-clipped-hatch-holes" ) );
@ -1545,8 +1600,8 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer
// create grid. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to
// generate strictly simple polygons needed by Gerber files and Fracture()
aRawPolys.BooleanSubtract( aRawPolys, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
DUMP_POLYS_TO_COPPER_LAYER( aRawPolys, In14_Cu, wxT( "after-hatching" ) );
aFillPolys.BooleanSubtract( aFillPolys, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In14_Cu, wxT( "after-hatching" ) );
return true;
}

View File

@ -88,8 +88,10 @@ private:
*/
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& aRawPolys );
const SHAPE_POLY_SET& aMaxExtents, SHAPE_POLY_SET& aFillPolys );
bool fillNonCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer,
const SHAPE_POLY_SET& aSmoothedOutline, SHAPE_POLY_SET& aFillPolys );
/**
* Function buildThermalSpokes
* Constructs a list of all thermal spokes for the given zone.
@ -108,7 +110,7 @@ private:
* @param aFillPolys: A reference to a SHAPE_POLY_SET buffer to store polygons with no holes
* (holes are linked to main outline by overlapping segments, and these polygons are shrunk
* by aZone->GetMinThickness() / 2 to be drawn with a outline thickness = aZone->GetMinThickness()
* aFinalPolys are polygons that will be drawn on screen and plotted
* aFillPolys are polygons that will be drawn on screen and plotted
*/
bool fillSingleZone( ZONE* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aFillPolys );
@ -116,11 +118,11 @@ private:
* for zones having the ZONE_FILL_MODE::ZONE_FILL_MODE::HATCH_PATTERN, create a grid pattern
* in filled areas of aZone, giving to the filled polygons a fill style like a grid
* @param aZone is the zone to modify
* @param aRawPolys: A reference to a SHAPE_POLY_SET buffer containing the initial
* @param aFillPolys: A reference to a SHAPE_POLY_SET buffer containing the initial
* filled areas, and after adding the grid pattern, the modified filled areas with holes
*/
bool addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LAYER_ID aDebugLayer,
SHAPE_POLY_SET& aRawPolys );
SHAPE_POLY_SET& aFillPolys );
BOARD* m_board;
SHAPE_POLY_SET m_boardOutline; // the board outlines, if exists