Check for drilled hole errors in Footprint Checker.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/18093
This commit is contained in:
Jeff Young 2024-05-27 13:51:02 +01:00
parent 2925d63c44
commit bc14a841cb
12 changed files with 71 additions and 60 deletions

View File

@ -158,6 +158,11 @@ public:
return false;
}
virtual bool HasDrilledHole() const
{
return false;
}
virtual bool IsTented() const
{
return false;

View File

@ -160,6 +160,7 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
m_DRCSeverities[ errorCode ] = RPT_SEVERITY_ERROR;
m_DRCSeverities[ DRCE_DRILLED_HOLES_COLOCATED ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_DRILLED_HOLES_TOO_CLOSE ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE;
m_DRCSeverities[ DRCE_PTH_IN_COURTYARD ] = RPT_SEVERITY_IGNORE;

View File

@ -158,10 +158,9 @@ void DIALOG_FOOTPRINT_CHECKER::runChecks()
} );
footprint->CheckShortingPads(
[&]( const PAD* aPadA, const PAD* aPadB, const VECTOR2I& aPosition )
[&]( const PAD* aPadA, const PAD* aPadB, int aErrorCode, const VECTOR2I& aPosition )
{
errorHandler( aPadA, aPadB, nullptr, DRCE_SHORTING_ITEMS, wxEmptyString,
aPosition );
errorHandler( aPadA, aPadB, nullptr, aErrorCode, wxEmptyString, aPosition );
} );
if( footprint->IsNetTie() )

View File

@ -649,29 +649,6 @@ DRC_CONSTRAINT DRC_ENGINE::EvalZoneConnection( const BOARD_ITEM* a, const BOARD_
}
bool hasDrilledHole( const BOARD_ITEM* aItem )
{
if( !aItem->HasHole() )
return false;
switch( aItem->Type() )
{
case PCB_VIA_T:
return true;
case PCB_PAD_T:
{
const PAD* pad = static_cast<const PAD*>( aItem );
return pad->GetDrillSizeX() == pad->GetDrillSizeY();
}
default:
return false;
}
}
DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM* a,
const BOARD_ITEM* b, PCB_LAYER_ID aLayer,
REPORTER* aReporter )
@ -1212,12 +1189,12 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
}
}
else if( c->constraint.m_Type == HOLE_TO_HOLE_CONSTRAINT
&& ( !hasDrilledHole( a ) || !hasDrilledHole( b ) ) )
&& ( !a->HasDrilledHole() || !b->HasDrilledHole() ) )
{
// Report non-drilled-holes as an implicit condition
if( aReporter )
{
const BOARD_ITEM* x = !hasDrilledHole( a ) ? a : b;
const BOARD_ITEM* x = !a->HasDrilledHole() ? a : b;
REPORT( wxString::Format( _( "%s is not a drilled hole; rule ignored." ),
x->GetItemDescription( this ) ) )

View File

@ -78,15 +78,18 @@ private:
static std::shared_ptr<SHAPE_CIRCLE> getDrilledHoleShape( BOARD_ITEM* aItem )
{
if( aItem->Type() == PCB_VIA_T )
if( aItem->HasDrilledHole() )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
return std::make_shared<SHAPE_CIRCLE>( via->GetCenter(), via->GetDrillValue() / 2 );
}
else if( aItem->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( aItem );
return std::make_shared<SHAPE_CIRCLE>( pad->GetPosition(), pad->GetDrillSize().x / 2 );
if( aItem->Type() == PCB_VIA_T )
{
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
return std::make_shared<SHAPE_CIRCLE>( via->GetCenter(), via->GetDrillValue() / 2 );
}
else if( aItem->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( aItem );
return std::make_shared<SHAPE_CIRCLE>( pad->GetPosition(), pad->GetDrillSize().x / 2 );
}
}
return std::make_shared<SHAPE_CIRCLE>( VECTOR2I( 0, 0 ), 0 );
@ -219,7 +222,7 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::Run()
return false; // DRC cancelled
// We only care about drilled (ie: round) holes
if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
if( pad->HasDrilledHole() )
{
std::shared_ptr<SHAPE_CIRCLE> holeShape = getDrilledHoleShape( pad );

View File

@ -3110,6 +3110,7 @@ void FOOTPRINT::CheckPads( const std::function<void( const PAD*, int,
void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const PAD*,
int aErrorCode,
const VECTOR2I& )>& aErrorHandler )
{
std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
@ -3120,13 +3121,7 @@ void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const P
for( PAD* other : Pads() )
{
if( other == pad || pad->SameLogicalPadAs( other ) )
continue;
if( alg::contains( netTiePads, other ) )
continue;
if( !( ( pad->GetLayerSet() & other->GetLayerSet() ) & LSET::AllCuMask() ).any() )
if( other == pad )
continue;
// store canonical order so we don't collide in both directions (a:b and b:a)
@ -3140,6 +3135,30 @@ void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const P
{
checkedPairs[ { a, b } ] = 1;
if( pad->HasDrilledHole() && other->HasDrilledHole() )
{
VECTOR2I pos = pad->GetPosition();
if( pad->GetPosition() == other->GetPosition() )
{
aErrorHandler( pad, other, DRCE_DRILLED_HOLES_COLOCATED, pos );
}
else
{
std::shared_ptr<SHAPE_SEGMENT> holeA = pad->GetEffectiveHoleShape();
std::shared_ptr<SHAPE_SEGMENT> holeB = other->GetEffectiveHoleShape();
if( holeA->Collide( holeB->GetSeg(), 0 ) )
aErrorHandler( pad, other, DRCE_DRILLED_HOLES_TOO_CLOSE, pos );
}
}
if( pad->SameLogicalPadAs( other ) || alg::contains( netTiePads, other ) )
continue;
if( !( ( pad->GetLayerSet() & other->GetLayerSet() ) & LSET::AllCuMask() ).any() )
continue;
if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
{
VECTOR2I pos;
@ -3147,7 +3166,7 @@ void FOOTPRINT::CheckShortingPads( const std::function<void( const PAD*, const P
SHAPE* otherShape = other->GetEffectiveShape().get();
if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
aErrorHandler( pad, other, pos );
aErrorHandler( pad, other, DRCE_SHORTING_ITEMS, pos );
}
}
}

View File

@ -461,8 +461,7 @@ public:
*
* @param aErrorHandler callback to handle the error messages generated
*/
void CheckShortingPads( const std::function<void( const PAD*,
const PAD*,
void CheckShortingPads( const std::function<void( const PAD*, const PAD*, int aErrorCode,
const VECTOR2I& )>& aErrorHandler );
/**

View File

@ -105,6 +105,11 @@ public:
return GetDrillSizeX() > 0 && GetDrillSizeY() > 0;
}
bool HasDrilledHole() const override
{
return HasHole() && GetDrillSizeX() == GetDrillSizeY();
}
bool IsLocked() const override;
/**

View File

@ -1563,10 +1563,10 @@ void PCB_IO_IPC2581::generateDrillLayers( wxXmlNode* aCadLayerNode )
{
for( PAD* pad : fp->Pads() )
{
if( pad->HasHole() && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
m_slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
else if( pad->HasHole() )
if( pad->HasDrilledHole() )
m_drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
else if( pad->HasHole() )
m_slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
}
}
@ -1703,7 +1703,7 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aPadNode, const PAD* aPad )
// Only handle round holes here because IPC2581 does not support non-round holes
// These will be handled in a slot layer
if( aPad->HasHole() && aPad->GetDrillSizeX() == aPad->GetDrillSizeY() )
if( aPad->HasDrilledHole() )
{
wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
padStackHoleNode->AddAttribute( "name", wxString::Format( "%s%d_%d",

View File

@ -420,6 +420,11 @@ public:
return true;
}
bool HasDrilledHole() const override
{
return m_viaType == VIATYPE::THROUGH;
}
std::shared_ptr<SHAPE_SEGMENT> GetEffectiveHoleShape() const override;
MINOPTMAX<int> GetWidthConstraint( wxString* aSource = nullptr ) const override;

View File

@ -298,15 +298,13 @@ static bool isEdge( const PNS::ITEM* aItem )
bool PNS_PCBNEW_RULE_RESOLVER::IsDrilledHole( const PNS::ITEM* aItem )
{
if( !isHole( aItem ) )
return false;
if( isHole( aItem ) )
{
if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aItem->Parent() ) )
return item->HasDrilledHole();
}
if( PAD* pad = dynamic_cast<PAD*>( aItem->Parent() ) )
return pad->GetDrillSizeX() && pad->GetDrillSizeX() == pad->GetDrillSizeY();
// Via holes are (currently) always round
return true;
return false;
}

View File

@ -1112,7 +1112,7 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
r->Flush();
}
if( a->HasHole() && b->HasHole() )
if( a->HasDrilledHole() && b->HasDrilledHole() )
{
if( !pageAdded )
{