Do footprint keepouts by courtyard.

Fixes https://gitlab.com/kicad/code/kicad/issues/6162
This commit is contained in:
Jeff Young 2020-10-25 15:02:49 +00:00
parent 07b8a03432
commit 386cefbe84
10 changed files with 142 additions and 84 deletions

View File

@ -120,7 +120,7 @@ typedef const INSPECTOR_FUNC& INSPECTOR; /// std::function passed to nested u
#define TRACK_LOCKED (1 << 18) ///< Pcbnew: track locked: protected from global deletion #define TRACK_LOCKED (1 << 18) ///< Pcbnew: track locked: protected from global deletion
#define TRACK_AR (1 << 19) ///< Pcbnew: autorouted track #define TRACK_AR (1 << 19) ///< Pcbnew: autorouted track
#define OBSOLETE_1 (1 << 20) ///< Not presently used #define OBSOLETE_1 (1 << 20) ///< Not presently used
#define OBSOLETE_2 (1 << 21) ///< Not presently used #define MALFORMED_COURTYARD (1 << 21)
#define BEGIN_ONPAD (1 << 22) ///< Pcbnew: flag set for track segment starting on a pad #define BEGIN_ONPAD (1 << 22) ///< Pcbnew: flag set for track segment starting on a pad
#define END_ONPAD (1 << 23) ///< Pcbnew: flag set for track segment ending on a pad #define END_ONPAD (1 << 23) ///< Pcbnew: flag set for track segment ending on a pad
#define HOLE_PROXY (1 << 24) ///< Indicates the BOARD_ITEM is a proxy for its hole #define HOLE_PROXY (1 << 24) ///< Indicates the BOARD_ITEM is a proxy for its hole

View File

@ -336,13 +336,11 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance )
m_fpAreaTop.RemoveAllContours(); m_fpAreaTop.RemoveAllContours();
m_fpAreaBottom.RemoveAllContours(); m_fpAreaBottom.RemoveAllContours();
if( aFootprint->BuildPolyCourtyard() ) aFootprint->BuildPolyCourtyards();
{ m_fpAreaTop = aFootprint->GetPolyCourtyardFront();
m_fpAreaTop = aFootprint->GetPolyCourtyardFront(); m_fpAreaBottom = aFootprint->GetPolyCourtyardBack();
m_fpAreaBottom = aFootprint->GetPolyCourtyardBack();
}
LSET layerMask; LSET layerMask;
if( aFootprint->GetLayer() == F_Cu ) if( aFootprint->GetLayer() == F_Cu )
layerMask.set( F_Cu ); layerMask.set( F_Cu );
@ -350,7 +348,7 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance )
if( aFootprint->GetLayer() == B_Cu ) if( aFootprint->GetLayer() == B_Cu )
layerMask.set( B_Cu ); layerMask.set( B_Cu );
EDA_RECT fpBBox = aFootprint->GetBoundingBox(); EDA_RECT fpBBox = aFootprint->GetBoundingBox();
fpBBox.Inflate( ( m_matrix.m_GridRouting / 2 ) + aFpClearance ); fpBBox.Inflate( ( m_matrix.m_GridRouting / 2 ) + aFpClearance );

View File

@ -1676,10 +1676,12 @@ std::shared_ptr<SHAPE> MODULE::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
} }
bool MODULE::BuildPolyCourtyard() void MODULE::BuildPolyCourtyards()
{ {
m_poly_courtyard_front.RemoveAllContours(); m_poly_courtyard_front.RemoveAllContours();
m_poly_courtyard_back.RemoveAllContours(); m_poly_courtyard_back.RemoveAllContours();
ClearFlags( MALFORMED_COURTYARD );
// Build the courtyard area from graphic items on the courtyard. // Build the courtyard area from graphic items on the courtyard.
// Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored. // Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored.
// Collect items: // Collect items:
@ -1695,22 +1697,22 @@ bool MODULE::BuildPolyCourtyard()
list_front.push_back( static_cast<PCB_SHAPE*>( item ) ); list_front.push_back( static_cast<PCB_SHAPE*>( item ) );
} }
// Note: if no item found on courtyard layers, return true.
// false is returned only when the shape defined on courtyard layers
// is not convertible to a polygon
if( !list_front.size() && !list_back.size() ) if( !list_front.size() && !list_back.size() )
return true; return;
wxString error_msg; wxString error_msg;
#define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */ #define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */
bool success = ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, if( !ConvertOutlineToPolygon( list_front, m_poly_courtyard_front,
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ); (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) )
if( success )
{ {
success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, SetFlags( MALFORMED_COURTYARD );
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ); }
if( !ConvertOutlineToPolygon( list_back, m_poly_courtyard_back,
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) )
{
SetFlags( MALFORMED_COURTYARD );
} }
if( !error_msg.IsEmpty() ) if( !error_msg.IsEmpty() )
@ -1719,8 +1721,6 @@ bool MODULE::BuildPolyCourtyard()
GetFPID().Format().wx_str(), GetFPID().Format().wx_str(),
error_msg ) ); error_msg ) );
} }
return success;
} }

View File

@ -668,11 +668,11 @@ public:
SHAPE_POLY_SET& GetPolyCourtyardBack() { return m_poly_courtyard_back; } SHAPE_POLY_SET& GetPolyCourtyardBack() { return m_poly_courtyard_back; }
/** /**
* Builds a complex polygon of the courtyard area from graphic items on the courtyard layer * Builds complex polygons of the courtyard areas from graphic items on the courtyard layers
* @return true if OK, or no courtyard defined, * @remark sets the MALFORMED_FRONT_COURTYARD and MALFORMED_BACK_COURTYARD status flags if
* false only if the polygon cannot be built due to a malformed courtyard shape * the given courtyard layer does not contain a (single) closed shape
*/ */
bool BuildPolyCourtyard(); void BuildPolyCourtyards();
virtual std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override; virtual std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override;

View File

@ -662,6 +662,8 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aTestTracksAgainstZones,
{ {
for( ZONE_CONTAINER* zone : module->Zones() ) for( ZONE_CONTAINER* zone : module->Zones() )
zone->CacheBoundingBox(); zone->CacheBoundingBox();
module->BuildPolyCourtyards();
} }
for( DRC_TEST_PROVIDER* provider : m_testProviders ) for( DRC_TEST_PROVIDER* provider : m_testProviders )

View File

@ -270,13 +270,21 @@ int DRC_TEST_PROVIDER::forEachGeometryItem( const std::vector<KICAD_T>& aTypes,
{ {
if( (zone->GetLayerSet() & aLayers).any() ) if( (zone->GetLayerSet() & aLayers).any() )
{ {
if( ! aFunc( zone ) ) if( !aFunc( zone ) )
return n; return n;
n++; n++;
} }
} }
} }
if( typeMask[ PCB_MODULE_T ] )
{
if( !aFunc( mod ) )
return n;
n++;
}
} }
return n; return n;

View File

@ -88,25 +88,7 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
if( !reportProgress( ii++, m_board->Modules().size(), delta ) ) if( !reportProgress( ii++, m_board->Modules().size(), delta ) )
return; return;
if( footprint->BuildPolyCourtyard() ) if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 )
{
if( footprint->GetPolyCourtyardFront().OutlineCount() == 0
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD ) )
continue;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD );
drcItem->SetItems( footprint );
reportViolation( drcItem, footprint->GetPosition());
}
else
{
footprint->GetPolyCourtyardFront().BuildBBoxCaches();
footprint->GetPolyCourtyardBack().BuildBBoxCaches();
}
}
else
{ {
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) ) if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) )
continue; continue;
@ -119,6 +101,21 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
drcItem->SetItems( footprint ); drcItem->SetItems( footprint );
reportViolation( drcItem, footprint->GetPosition()); reportViolation( drcItem, footprint->GetPosition());
} }
else if( footprint->GetPolyCourtyardFront().OutlineCount() == 0
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD ) )
continue;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD );
drcItem->SetItems( footprint );
reportViolation( drcItem, footprint->GetPosition());
}
else
{
footprint->GetPolyCourtyardFront().BuildBBoxCaches();
footprint->GetPolyCourtyardBack().BuildBBoxCaches();
}
} }
} }

View File

@ -177,7 +177,9 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
// If not, the pads bounding box will be used. // If not, the pads bounding box will be used.
bool useFpPadsBbox = true; bool useFpPadsBbox = true;
if( footprint->BuildPolyCourtyard() ) footprint->BuildPolyCourtyards();
if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) == 0 )
{ {
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_COURTYARD ); gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_COURTYARD );

View File

@ -213,18 +213,97 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self ); PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
ZONE_CONTAINER* zone = nullptr;
if( !item ) if( !item )
return; return;
auto insideZone =
[&]( ZONE_CONTAINER* zone ) -> bool
{
if( !zone )
return false;
if( !zone->GetCachedBoundingBox().Intersects( item->GetBoundingBox() ) )
return false;
if( item->GetFlags() & HOLE_PROXY )
{
if( item->Type() == PCB_PAD_T )
{
D_PAD* pad = static_cast<D_PAD*>( item );
const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape();
return zone->Outline()->Collide( holeShape );
}
else if( item->Type() == PCB_VIA_T )
{
VIA* via = static_cast<VIA*>( item );
const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() );
return zone->Outline()->Collide( &holeShape );
}
return false;
}
if( item->Type() == PCB_MODULE_T )
{
MODULE* footprint = static_cast<MODULE*>( item );
if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 )
{
aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
return false;
}
if( ( zone->GetLayerSet() & LSET::FrontMask() ).any() )
{
SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardFront();
if( courtyard.OutlineCount() == 0 )
{
aCtx->ReportError( _( "Footprint has no front courtyard." ) );
return false;
}
else
{
return zone->Outline()->Collide( &courtyard.Outline( 0 ) );
}
}
if( ( zone->GetLayerSet() & LSET::BackMask() ).any() )
{
SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyardBack();
if( courtyard.OutlineCount() == 0 )
{
aCtx->ReportError( _( "Footprint has no back courtyard." ) );
return false;
}
else
{
return zone->Outline()->Collide( &courtyard.Outline( 0 ) );
}
}
return false;
}
std::shared_ptr<SHAPE> shape = item->GetEffectiveShape( context->GetLayer() );
return zone->Outline()->Collide( shape.get() );
};
if( arg->AsString() == "A" ) if( arg->AsString() == "A" )
{ {
zone = dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 0 ) ); if( insideZone( dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 0 ) ) ) )
result->Set( 1.0 );
} }
else if( arg->AsString() == "B" ) else if( arg->AsString() == "B" )
{ {
zone = dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 1 ) ); if( insideZone( dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 1 ) ) ) )
result->Set( 1.0 );
} }
else else
{ {
@ -232,42 +311,12 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
{ {
if( candidate->GetZoneName().Matches( arg->AsString() ) ) if( candidate->GetZoneName().Matches( arg->AsString() ) )
{ {
zone = candidate; if( insideZone( candidate ) )
break; {
}
}
}
if( zone )
{
if( !zone->GetCachedBoundingBox().Intersects( item->GetBoundingBox() ) )
return;
if( item->GetFlags() & HOLE_PROXY )
{
if( item->Type() == PCB_PAD_T )
{
D_PAD* pad = static_cast<D_PAD*>( item );
const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape();
if( zone->Outline()->Collide( holeShape ) )
result->Set( 1.0 ); result->Set( 1.0 );
return;
}
} }
else if( item->Type() == PCB_VIA_T )
{
VIA* via = static_cast<VIA*>( item );
const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() );
if( zone->Outline()->Collide( &holeShape ) )
result->Set( 1.0 );
}
}
else
{
std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( context->GetLayer() );
if( zone->Outline()->Collide( itemShape.get() ) )
result->Set( 1.0 );
} }
} }
} }

View File

@ -408,6 +408,8 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
{ {
for( ZONE_CONTAINER* zone : module->Zones() ) for( ZONE_CONTAINER* zone : module->Zones() )
zone->CacheBoundingBox(); zone->CacheBoundingBox();
module->BuildPolyCourtyards();
} }
if( item->Type() == PCB_TRACE_T ) if( item->Type() == PCB_TRACE_T )
@ -570,7 +572,7 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
r->Report( "" ); r->Report( "" );
if( constraint.m_DisallowFlags ) if( constraint.m_DisallowFlags )
r->Report( _( "Item disallowed at current location." ) ); r->Report( _( "Item <b>disallowed</b> at current location." ) );
else else
r->Report( _( "Item allowed at current location." ) ); r->Report( _( "Item allowed at current location." ) );
} }