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_AR (1 << 19) ///< Pcbnew: autorouted track
#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 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

View File

@ -336,13 +336,11 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance )
m_fpAreaTop.RemoveAllContours();
m_fpAreaBottom.RemoveAllContours();
if( aFootprint->BuildPolyCourtyard() )
{
m_fpAreaTop = aFootprint->GetPolyCourtyardFront();
m_fpAreaBottom = aFootprint->GetPolyCourtyardBack();
}
aFootprint->BuildPolyCourtyards();
m_fpAreaTop = aFootprint->GetPolyCourtyardFront();
m_fpAreaBottom = aFootprint->GetPolyCourtyardBack();
LSET layerMask;
LSET layerMask;
if( aFootprint->GetLayer() == F_Cu )
layerMask.set( F_Cu );
@ -350,7 +348,7 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance )
if( aFootprint->GetLayer() == B_Cu )
layerMask.set( B_Cu );
EDA_RECT fpBBox = aFootprint->GetBoundingBox();
EDA_RECT fpBBox = aFootprint->GetBoundingBox();
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_back.RemoveAllContours();
ClearFlags( MALFORMED_COURTYARD );
// Build the courtyard area from graphic items on the courtyard.
// Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored.
// Collect items:
@ -1695,22 +1697,22 @@ bool MODULE::BuildPolyCourtyard()
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() )
return true;
return;
wxString error_msg;
#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,
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg );
if( success )
if( !ConvertOutlineToPolygon( list_front, m_poly_courtyard_front,
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) )
{
success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back,
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg );
SetFlags( MALFORMED_COURTYARD );
}
if( !ConvertOutlineToPolygon( list_back, m_poly_courtyard_back,
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) )
{
SetFlags( MALFORMED_COURTYARD );
}
if( !error_msg.IsEmpty() )
@ -1719,8 +1721,6 @@ bool MODULE::BuildPolyCourtyard()
GetFPID().Format().wx_str(),
error_msg ) );
}
return success;
}

View File

@ -668,11 +668,11 @@ public:
SHAPE_POLY_SET& GetPolyCourtyardBack() { return m_poly_courtyard_back; }
/**
* Builds a complex polygon of the courtyard area from graphic items on the courtyard layer
* @return true if OK, or no courtyard defined,
* false only if the polygon cannot be built due to a malformed courtyard shape
* Builds complex polygons of the courtyard areas from graphic items on the courtyard layers
* @remark sets the MALFORMED_FRONT_COURTYARD and MALFORMED_BACK_COURTYARD status flags if
* 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;

View File

@ -662,6 +662,8 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aTestTracksAgainstZones,
{
for( ZONE_CONTAINER* zone : module->Zones() )
zone->CacheBoundingBox();
module->BuildPolyCourtyards();
}
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( ! aFunc( zone ) )
if( !aFunc( zone ) )
return n;
n++;
}
}
}
if( typeMask[ PCB_MODULE_T ] )
{
if( !aFunc( mod ) )
return n;
n++;
}
}
return n;

View File

@ -88,25 +88,7 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
if( !reportProgress( ii++, m_board->Modules().size(), delta ) )
return;
if( footprint->BuildPolyCourtyard() )
{
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( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 )
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) )
continue;
@ -119,6 +101,21 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
drcItem->SetItems( footprint );
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.
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 );

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 );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
ZONE_CONTAINER* zone = nullptr;
if( !item )
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" )
{
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" )
{
zone = dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 1 ) );
if( insideZone( dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 1 ) ) ) )
result->Set( 1.0 );
}
else
{
@ -232,42 +311,12 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
{
if( candidate->GetZoneName().Matches( arg->AsString() ) )
{
zone = 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 ) )
if( insideZone( candidate ) )
{
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() )
zone->CacheBoundingBox();
module->BuildPolyCourtyards();
}
if( item->Type() == PCB_TRACE_T )
@ -570,7 +572,7 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
r->Report( "" );
if( constraint.m_DisallowFlags )
r->Report( _( "Item disallowed at current location." ) );
r->Report( _( "Item <b>disallowed</b> at current location." ) );
else
r->Report( _( "Item allowed at current location." ) );
}