Do footprint keepouts by courtyard.
Fixes https://gitlab.com/kicad/code/kicad/issues/6162
This commit is contained in:
parent
07b8a03432
commit
386cefbe84
|
@ -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
|
||||
|
|
|
@ -336,11 +336,9 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance )
|
|||
m_fpAreaTop.RemoveAllContours();
|
||||
m_fpAreaBottom.RemoveAllContours();
|
||||
|
||||
if( aFootprint->BuildPolyCourtyard() )
|
||||
{
|
||||
aFootprint->BuildPolyCourtyards();
|
||||
m_fpAreaTop = aFootprint->GetPolyCourtyardFront();
|
||||
m_fpAreaBottom = aFootprint->GetPolyCourtyardBack();
|
||||
}
|
||||
|
||||
LSET layerMask;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -88,9 +88,20 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
|
|||
if( !reportProgress( ii++, m_board->Modules().size(), delta ) )
|
||||
return;
|
||||
|
||||
if( footprint->BuildPolyCourtyard() )
|
||||
if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 )
|
||||
{
|
||||
if( footprint->GetPolyCourtyardFront().OutlineCount() == 0
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) )
|
||||
continue;
|
||||
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(not a closed shape)" ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( footprint );
|
||||
reportViolation( drcItem, footprint->GetPosition());
|
||||
}
|
||||
else if( footprint->GetPolyCourtyardFront().OutlineCount() == 0
|
||||
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MISSING_COURTYARD ) )
|
||||
|
@ -106,20 +117,6 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
|
|||
footprint->GetPolyCourtyardBack().BuildBBoxCaches();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) )
|
||||
continue;
|
||||
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(not a closed shape)" ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( footprint );
|
||||
reportViolation( drcItem, footprint->GetPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -213,35 +213,18 @@ 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;
|
||||
|
||||
if( arg->AsString() == "A" )
|
||||
auto insideZone =
|
||||
[&]( ZONE_CONTAINER* zone ) -> bool
|
||||
{
|
||||
zone = dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 0 ) );
|
||||
}
|
||||
else if( arg->AsString() == "B" )
|
||||
{
|
||||
zone = dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 1 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( ZONE_CONTAINER* candidate : item->GetBoard()->Zones() )
|
||||
{
|
||||
if( candidate->GetZoneName().Matches( arg->AsString() ) )
|
||||
{
|
||||
zone = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !zone )
|
||||
return false;
|
||||
|
||||
if( zone )
|
||||
{
|
||||
if( !zone->GetCachedBoundingBox().Intersects( item->GetBoundingBox() ) )
|
||||
return;
|
||||
return false;
|
||||
|
||||
if( item->GetFlags() & HOLE_PROXY )
|
||||
{
|
||||
|
@ -250,25 +233,91 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
|
|||
D_PAD* pad = static_cast<D_PAD*>( item );
|
||||
const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape();
|
||||
|
||||
if( zone->Outline()->Collide( holeShape ) )
|
||||
result->Set( 1.0 );
|
||||
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() );
|
||||
|
||||
if( zone->Outline()->Collide( &holeShape ) )
|
||||
result->Set( 1.0 );
|
||||
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
|
||||
{
|
||||
std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( context->GetLayer() );
|
||||
return zone->Outline()->Collide( &courtyard.Outline( 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( zone->Outline()->Collide( itemShape.get() ) )
|
||||
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( insideZone( dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 0 ) ) ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
else if( arg->AsString() == "B" )
|
||||
{
|
||||
if( insideZone( dynamic_cast<ZONE_CONTAINER*>( context->GetItem( 1 ) ) ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( ZONE_CONTAINER* candidate : item->GetBoard()->Zones() )
|
||||
{
|
||||
if( candidate->GetZoneName().Matches( arg->AsString() ) )
|
||||
{
|
||||
if( insideZone( candidate ) )
|
||||
{
|
||||
result->Set( 1.0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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." ) );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue