mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-20 00:21:31 +00:00
Simplify testing of multiple pads with same number.
We now test each hole against the combined copper of all pads with the same number minus the other holes. Also fixes a bug with "handled" flag not getting reset between layers of padstacks. Fixes https://gitlab.com/kicad/code/kicad/-/issues/19521
This commit is contained in:
parent
db601c58cc
commit
a41a9ea51f
@ -88,8 +88,6 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run()
|
||||
if( !reportPhase( _( "Checking pad & via annular rings..." ) ) )
|
||||
return false; // DRC cancelled
|
||||
|
||||
int maxError = m_drcEngine->GetBoard()->GetDesignSettings().m_MaxError;
|
||||
|
||||
auto calcEffort =
|
||||
[]( BOARD_ITEM* item ) -> size_t
|
||||
{
|
||||
@ -143,6 +141,120 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run()
|
||||
}
|
||||
};
|
||||
|
||||
auto checkPadAnnularWidth =
|
||||
[]( PAD* pad, PCB_LAYER_ID aLayer, DRC_CONSTRAINT& constraint,
|
||||
const std::vector<const PAD*>& sameNumPads,
|
||||
int* aMinAnnularWidth, int* aMaxAnnularWidth )
|
||||
{
|
||||
int annularWidth = 0;
|
||||
bool handled = false;
|
||||
|
||||
if( pad->GetOffset( aLayer ) == VECTOR2I( 0, 0 ) )
|
||||
{
|
||||
VECTOR2I padSize = pad->GetSize( aLayer );
|
||||
|
||||
switch( pad->GetShape( aLayer ) )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
annularWidth = ( padSize.x - pad->GetDrillSizeX() ) / 2;
|
||||
|
||||
// If there are more pads with the same number then we'll still need to
|
||||
// run the more generalised checks below.
|
||||
handled = sameNumPads.empty();
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::CHAMFERED_RECT:
|
||||
if( pad->GetChamferRectRatio( aLayer ) > 0.30 )
|
||||
break;
|
||||
|
||||
KI_FALLTHROUGH;
|
||||
|
||||
case PAD_SHAPE::OVAL:
|
||||
case PAD_SHAPE::RECTANGLE:
|
||||
case PAD_SHAPE::ROUNDRECT:
|
||||
annularWidth = std::min( padSize.x - pad->GetDrillSizeX(),
|
||||
padSize.y - pad->GetDrillSizeY() ) / 2;
|
||||
|
||||
// If there are more pads with the same number then we'll still need to
|
||||
// run the more generalised checks below.
|
||||
handled = sameNumPads.empty();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !handled )
|
||||
{
|
||||
// Slow (but general purpose) method.
|
||||
int maxError = pad->GetBoard()->GetDesignSettings().m_MaxError;
|
||||
SEG::ecoord dist_sq;
|
||||
SHAPE_POLY_SET padOutline;
|
||||
std::shared_ptr<SHAPE_SEGMENT> slot = pad->GetEffectiveHoleShape();
|
||||
|
||||
pad->TransformShapeToPolygon( padOutline, aLayer, 0, maxError, ERROR_INSIDE );
|
||||
|
||||
if( sameNumPads.empty() )
|
||||
{
|
||||
if( !padOutline.Collide( pad->GetPosition() ) )
|
||||
{
|
||||
// Hole outside pad
|
||||
annularWidth = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable is-inside test in SquaredDistance
|
||||
padOutline.Outline( 0 ).SetClosed( false );
|
||||
|
||||
dist_sq = padOutline.SquaredDistanceToSeg( slot->GetSeg() );
|
||||
annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2;
|
||||
}
|
||||
}
|
||||
else if( constraint.Value().HasMin()
|
||||
&& ( annularWidth < constraint.Value().Min() ) )
|
||||
{
|
||||
SHAPE_POLY_SET aggregatePadOutline = padOutline;
|
||||
SHAPE_POLY_SET otherPadHoles;
|
||||
SHAPE_POLY_SET slotPolygon;
|
||||
|
||||
slot->TransformToPolygon( slotPolygon, 0, ERROR_INSIDE );
|
||||
|
||||
for( const PAD* sameNumPad : sameNumPads )
|
||||
{
|
||||
// Construct the full pad with outline and hole.
|
||||
sameNumPad->TransformShapeToPolygon( aggregatePadOutline,
|
||||
PADSTACK::ALL_LAYERS, 0,
|
||||
maxError, ERROR_OUTSIDE );
|
||||
|
||||
sameNumPad->TransformHoleToPolygon( otherPadHoles, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
|
||||
aggregatePadOutline.BooleanSubtract( otherPadHoles );
|
||||
|
||||
if( !aggregatePadOutline.Collide( pad->GetPosition() ) )
|
||||
{
|
||||
// Hole outside pad
|
||||
annularWidth = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable is-inside test in SquaredDistance
|
||||
aggregatePadOutline.Outline( 0 ).SetClosed( false );
|
||||
|
||||
dist_sq = aggregatePadOutline.SquaredDistanceToSeg( slot->GetSeg() );
|
||||
annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*aMaxAnnularWidth = std::max( *aMaxAnnularWidth, annularWidth );
|
||||
*aMinAnnularWidth = std::min( *aMinAnnularWidth, annularWidth );
|
||||
};
|
||||
|
||||
auto checkAnnularWidth =
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
@ -159,167 +271,41 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run()
|
||||
bool fail_min = false;
|
||||
bool fail_max = false;
|
||||
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
||||
int drill = via->GetDrillValue();
|
||||
int drill = via->GetDrillValue();
|
||||
|
||||
via->Padstack().ForEachUniqueLayer(
|
||||
[&]( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
int layerWidth = ( via->GetWidth( aLayer ) - drill ) / 2;
|
||||
minAnnularWidth = std::min( minAnnularWidth, layerWidth );
|
||||
maxAnnularWidth = std::max( maxAnnularWidth, layerWidth );
|
||||
} );
|
||||
[&]( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
int layerWidth = ( via->GetWidth( aLayer ) - drill ) / 2;
|
||||
minAnnularWidth = std::min( minAnnularWidth, layerWidth );
|
||||
maxAnnularWidth = std::max( maxAnnularWidth, layerWidth );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
|
||||
case PCB_PAD_T:
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
bool handled = false;
|
||||
|
||||
if( !pad->HasHole() || pad->GetAttribute() != PAD_ATTRIB::PTH )
|
||||
return true;
|
||||
|
||||
std::vector<const PAD*> sameNumPads;
|
||||
|
||||
const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( pad->GetParent() );
|
||||
|
||||
if( fp )
|
||||
if( const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( pad->GetParent() ) )
|
||||
sameNumPads = fp->GetPads( pad->GetNumber(), pad );
|
||||
|
||||
pad->Padstack().ForEachUniqueLayer(
|
||||
[&]( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
int annularWidth = 0;
|
||||
|
||||
if( pad->GetOffset( aLayer ) == VECTOR2I( 0, 0 ) )
|
||||
[&]( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
VECTOR2I padSize = pad->GetSize( aLayer );
|
||||
|
||||
switch( pad->GetShape( aLayer ) )
|
||||
{
|
||||
case PAD_SHAPE::CIRCLE:
|
||||
annularWidth = ( padSize.x - pad->GetDrillSizeX() ) / 2;
|
||||
|
||||
// If there are more pads with the same number. Check to see if the
|
||||
// pad is embedded inside another pad with the same number below.
|
||||
if( sameNumPads.empty() )
|
||||
handled = true;
|
||||
|
||||
break;
|
||||
|
||||
case PAD_SHAPE::CHAMFERED_RECT:
|
||||
if( pad->GetChamferRectRatio( aLayer ) > 0.30 )
|
||||
break;
|
||||
|
||||
KI_FALLTHROUGH;
|
||||
|
||||
case PAD_SHAPE::OVAL:
|
||||
case PAD_SHAPE::RECTANGLE:
|
||||
case PAD_SHAPE::ROUNDRECT:
|
||||
annularWidth = std::min( padSize.x - pad->GetDrillSizeX(),
|
||||
padSize.y - pad->GetDrillSizeY() ) / 2;
|
||||
|
||||
// If there are more pads with the same number. Check to see if the
|
||||
// pad is embedded inside another pad with the same number below.
|
||||
if( sameNumPads.empty() )
|
||||
handled = true;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !handled )
|
||||
{
|
||||
// Slow (but general purpose) method.
|
||||
SEG::ecoord dist_sq;
|
||||
SHAPE_POLY_SET padOutline;
|
||||
std::shared_ptr<SHAPE_SEGMENT> slot = pad->GetEffectiveHoleShape();
|
||||
|
||||
pad->TransformShapeToPolygon( padOutline, aLayer, 0, maxError,
|
||||
ERROR_INSIDE );
|
||||
|
||||
if( sameNumPads.empty() )
|
||||
{
|
||||
if( !padOutline.Collide( pad->GetPosition() ) )
|
||||
{
|
||||
// Hole outside pad
|
||||
annularWidth = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable is-inside test in SquaredDistance
|
||||
padOutline.Outline( 0 ).SetClosed( false );
|
||||
|
||||
dist_sq = padOutline.SquaredDistanceToSeg( slot->GetSeg() );
|
||||
annularWidth = sqrt( dist_sq ) - slot->GetWidth() / 2;
|
||||
}
|
||||
}
|
||||
else if( constraint.Value().HasMin()
|
||||
&& ( annularWidth < constraint.Value().Min() ) )
|
||||
{
|
||||
SHAPE_POLY_SET otherPadOutline;
|
||||
SHAPE_POLY_SET otherPadHoles;
|
||||
SHAPE_POLY_SET slotPolygon;
|
||||
|
||||
slot->TransformToPolygon( slotPolygon, 0, ERROR_INSIDE );
|
||||
|
||||
for( const PAD* sameNumPad : sameNumPads )
|
||||
{
|
||||
// Construct the full pad with outline and hole.
|
||||
sameNumPad->TransformShapeToPolygon(
|
||||
otherPadOutline, PADSTACK::ALL_LAYERS, 0, maxError,
|
||||
ERROR_OUTSIDE );
|
||||
|
||||
sameNumPad->TransformHoleToPolygon(
|
||||
otherPadHoles, 0, maxError, ERROR_INSIDE );
|
||||
}
|
||||
|
||||
otherPadOutline.BooleanSubtract( otherPadHoles );
|
||||
|
||||
// If the pad hole under test intersects with another pad outline,
|
||||
// the annular width calculated above is used.
|
||||
bool intersects = false;
|
||||
|
||||
for( int i = 0; i < otherPadOutline.OutlineCount() && !intersects; i++ )
|
||||
{
|
||||
intersects |= slotPolygon.COutline( 0 ).Intersects( otherPadOutline.COutline( i ) );
|
||||
|
||||
for( int j = 0; j < otherPadOutline.HoleCount( i ) && !intersects; j++ )
|
||||
{
|
||||
intersects |= slotPolygon.COutline( 0 ).Intersects( otherPadOutline.CHole( i, j ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( !intersects )
|
||||
{
|
||||
// Determine the effective annular width if the pad hole under
|
||||
// test lies withing the boundary of another pad outline.
|
||||
int effectiveWidth = std::numeric_limits<int>::max();
|
||||
|
||||
for( int ii = 0; ii < otherPadOutline.OutlineCount(); ii++ )
|
||||
{
|
||||
if( slot->Collide( &otherPadOutline.Outline( ii ), 0 ) )
|
||||
{
|
||||
if( effectiveWidth > annularWidth )
|
||||
annularWidth = effectiveWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maxAnnularWidth = std::max( maxAnnularWidth, annularWidth );
|
||||
minAnnularWidth = std::min( minAnnularWidth, annularWidth );
|
||||
} );
|
||||
checkPadAnnularWidth( pad, aLayer, constraint, sameNumPads,
|
||||
&minAnnularWidth, &maxAnnularWidth );
|
||||
} );
|
||||
|
||||
break;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user