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_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
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 )
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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." ) );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue