Add implicit rule generation for keepout areas.
Also implements collision detection for SHAPE_POLY_SET. Fixes https://gitlab.com/kicad/code/kicad/issues/6105
This commit is contained in:
parent
7d09681f9e
commit
9ff49277e1
|
@ -122,7 +122,7 @@ typedef const INSPECTOR_FUNC& INSPECTOR; /// std::function passed to nested u
|
|||
#define OBSOLETE_2 (1 << 21) ///< Not presently used
|
||||
#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 OBSOLETE_3 (1 << 24) ///< Not presently used
|
||||
#define HOLE_PROXY (1 << 24) ///< Indicates the BOARD_ITEM is a proxy for its hole
|
||||
#define IS_ROLLOVER (1 << 25) ///< Rollover active. Used for hyperlink highlighting.
|
||||
#define BRIGHTENED (1 << 26) ///< item is drawn with a bright contour
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#define __SHAPE_ARC_H
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <include/convert_to_biu.h>
|
||||
#include <math/vector2d.h> // for VECTOR2I
|
||||
|
||||
class SHAPE_LINE_CHAIN;
|
||||
|
@ -134,14 +135,14 @@ public:
|
|||
/**
|
||||
* Constructs a SHAPE_LINE_CHAIN of segments from a given arc
|
||||
* @param aAccuracy maximum divergence from true arc given in internal units
|
||||
* ** Note that the default of 500.0 here is given using ARC_DEF_HIGH_ACCURACY
|
||||
* for pcbnew units. This is to allow common geometry collision functions
|
||||
* ** Note that the default is ARC_HIGH_DEF in PCBNew units
|
||||
* This is to allow common geometry collision functions
|
||||
* Other programs should call this using explicit accuracy values
|
||||
* TODO: unify KiCad internal units
|
||||
*
|
||||
* @return a SHAPE_LINE_CHAIN
|
||||
*/
|
||||
const SHAPE_LINE_CHAIN ConvertToPolyline( double aAccuracy = 500.0 ) const;
|
||||
const SHAPE_LINE_CHAIN ConvertToPolyline( double aAccuracy = 0.005 * PCB_IU_PER_MM ) const;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -1039,6 +1039,22 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
*/
|
||||
bool PointOnEdge( const VECTOR2I& aP ) const;
|
||||
|
||||
/**
|
||||
* Function Collide()
|
||||
*
|
||||
* Checks if the boundary of shape (this) lies closer to the shape aShape than aClearance,
|
||||
* indicating a collision.
|
||||
* @param aShape shape to check collision against
|
||||
* @param aClearance minimum clearance
|
||||
* @param aActual [out] an optional pointer to an int to store the actual distance in the
|
||||
* event of a collision.
|
||||
* @param aLocation [out] an option pointer to a point to store a nearby location in the
|
||||
* event of a collision.
|
||||
* @return true, if there is a collision.
|
||||
*/
|
||||
bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr,
|
||||
VECTOR2I* aLocation = nullptr ) const override;
|
||||
|
||||
/**
|
||||
* Function Collide
|
||||
* Checks whether the point aP is either inside or on the edge of the polygon set.
|
||||
|
|
|
@ -50,7 +50,10 @@
|
|||
#include <math/util.h> // for KiROUND, rescale
|
||||
#include <math/vector2d.h> // for VECTOR2I, VECTOR2D, VECTOR2
|
||||
#include <md5_hash.h>
|
||||
|
||||
#include <libs/kimath/include/geometry/shape_segment.h>
|
||||
#include <libs/kimath/include/geometry/shape_circle.h>
|
||||
#include <libs/kimath/include/geometry/shape_simple.h>
|
||||
#include <libs/kimath/include/geometry/shape_compound.h>
|
||||
|
||||
using namespace ClipperLib;
|
||||
|
||||
|
@ -1267,6 +1270,84 @@ bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_POLY_SET::Collide( const SHAPE* aShape, int aClearance, int* aActual,
|
||||
VECTOR2I* aLocation ) const
|
||||
{
|
||||
// A couple of simple cases are worth trying before we fall back on triangulation.
|
||||
|
||||
if( aShape->Type() == SH_SEGMENT )
|
||||
{
|
||||
const SHAPE_SEGMENT* segment = static_cast<const SHAPE_SEGMENT*>( aShape );
|
||||
int extra = segment->GetWidth() / 2;
|
||||
|
||||
if( Collide( segment->GetSeg(), aClearance + extra, aActual, aLocation ) )
|
||||
{
|
||||
if( aActual )
|
||||
*aActual = std::max( 0, *aActual - extra );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aShape->Type() == SH_CIRCLE )
|
||||
{
|
||||
const SHAPE_CIRCLE* circle = static_cast<const SHAPE_CIRCLE*>( aShape );
|
||||
int extra = circle->GetRadius();
|
||||
|
||||
if( Collide( circle->GetCenter(), aClearance + extra, aActual, aLocation ) )
|
||||
{
|
||||
if( aActual )
|
||||
*aActual = std::max( 0, *aActual - extra );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const_cast<SHAPE_POLY_SET*>( this )->CacheTriangulation( true );
|
||||
|
||||
int actual = INT_MAX;
|
||||
VECTOR2I location;
|
||||
|
||||
for( auto& tpoly : m_triangulatedPolys )
|
||||
{
|
||||
for ( auto& tri : tpoly->Triangles() )
|
||||
{
|
||||
int triActual;
|
||||
VECTOR2I triLocation;
|
||||
|
||||
if( aShape->Collide( &tri, aClearance, &triActual, &triLocation ) )
|
||||
{
|
||||
if( !aActual && !aLocation )
|
||||
return true;
|
||||
|
||||
if( triActual < actual )
|
||||
{
|
||||
actual = triActual;
|
||||
location = triLocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( actual < INT_MAX )
|
||||
{
|
||||
if( aActual )
|
||||
*aActual = std::max( 0, actual );
|
||||
|
||||
if( aLocation )
|
||||
*aLocation = location;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_POLY_SET::RemoveAllContours()
|
||||
{
|
||||
m_polys.clear();
|
||||
|
@ -2148,8 +2229,10 @@ bool SHAPE_POLY_SET::HasIndexableSubshapes() const
|
|||
size_t SHAPE_POLY_SET::GetIndexableSubshapeCount() const
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
for( auto& t : m_triangulatedPolys )
|
||||
n += t->GetTriangleCount();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <drc/drc_rule.h>
|
||||
#include <drc/drc_rule_condition.h>
|
||||
#include <drc/drc_test_provider.h>
|
||||
#include <class_track.h>
|
||||
|
||||
void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line )
|
||||
{
|
||||
|
@ -140,9 +141,9 @@ void DRC_ENGINE::loadImplicitRules()
|
|||
|
||||
// 2) micro-via specific defaults (new DRC doesn't treat microvias in any special way)
|
||||
|
||||
DRC_RULE* uViaRule = createImplicitRule( _( "board setup micro-via constraints" ));
|
||||
DRC_RULE* uViaRule = createImplicitRule( _( "board setup micro-via constraints" ) );
|
||||
|
||||
uViaRule->m_Condition = new DRC_RULE_CONDITION ( "A.Via_Type == 'micro_via'" );
|
||||
uViaRule->m_Condition = new DRC_RULE_CONDITION( "A.Via_Type == 'micro_via'" );
|
||||
|
||||
DRC_CONSTRAINT uViaDrillConstraint( DRC_CONSTRAINT_TYPE_HOLE_SIZE );
|
||||
uViaDrillConstraint.Value().SetMin( bds.m_MicroViasMinDrill );
|
||||
|
@ -161,9 +162,9 @@ void DRC_ENGINE::loadImplicitRules()
|
|||
|
||||
if( !bds.m_BlindBuriedViaAllowed )
|
||||
{
|
||||
DRC_RULE* bbViaRule = createImplicitRule( _( "board setup constraints" ));
|
||||
DRC_RULE* bbViaRule = createImplicitRule( _( "board setup constraints" ) );
|
||||
|
||||
bbViaRule->m_Condition = new DRC_RULE_CONDITION ( "A.Via_Type == 'buried_via'" );
|
||||
bbViaRule->m_Condition = new DRC_RULE_CONDITION( "A.Via_Type == 'buried_via'" );
|
||||
|
||||
DRC_CONSTRAINT disallowConstraint( DRC_CONSTRAINT_TYPE_DISALLOW );
|
||||
disallowConstraint.m_DisallowFlags = DRC_DISALLOW_BB_VIAS;
|
||||
|
@ -319,6 +320,67 @@ void DRC_ENGINE::loadImplicitRules()
|
|||
for( DRC_RULE* ncRule : netclassItemSpecificRules )
|
||||
addRule( ncRule );
|
||||
|
||||
// 3) keepout area rules
|
||||
|
||||
auto addKeepoutConstraint =
|
||||
[&rule]( int aConstraint )
|
||||
{
|
||||
DRC_CONSTRAINT disallowConstraint( DRC_CONSTRAINT_TYPE_DISALLOW );
|
||||
disallowConstraint.m_DisallowFlags = aConstraint;
|
||||
rule->AddConstraint( disallowConstraint );
|
||||
};
|
||||
|
||||
auto isKeepoutZone =
|
||||
[]( ZONE_CONTAINER* aZone )
|
||||
{
|
||||
return aZone->GetIsRuleArea() && ( aZone->GetDoNotAllowTracks()
|
||||
|| aZone->GetDoNotAllowVias()
|
||||
|| aZone->GetDoNotAllowPads()
|
||||
|| aZone->GetDoNotAllowCopperPour()
|
||||
|| aZone->GetDoNotAllowFootprints() );
|
||||
};
|
||||
|
||||
std::vector<ZONE_CONTAINER*> keepoutZones;
|
||||
|
||||
for( ZONE_CONTAINER* zone : m_board->Zones() )
|
||||
{
|
||||
if( isKeepoutZone( zone ) )
|
||||
keepoutZones.push_back( zone );
|
||||
}
|
||||
|
||||
for( MODULE* footprint : m_board->Modules() )
|
||||
{
|
||||
for( ZONE_CONTAINER* zone : footprint->Zones() )
|
||||
{
|
||||
if( isKeepoutZone( zone ) )
|
||||
keepoutZones.push_back( zone );
|
||||
}
|
||||
}
|
||||
|
||||
for( ZONE_CONTAINER* zone : keepoutZones )
|
||||
{
|
||||
if( zone->GetZoneName().IsEmpty() )
|
||||
zone->SetZoneName( KIID().AsString() );
|
||||
|
||||
rule = createImplicitRule( _( "keepout area" ) );
|
||||
rule->m_Condition = new DRC_RULE_CONDITION( wxString::Format( "A.insideArea('%s')",
|
||||
zone->GetZoneName() ) );
|
||||
if( zone->GetDoNotAllowTracks() )
|
||||
addKeepoutConstraint( DRC_DISALLOW_TRACKS );
|
||||
|
||||
if( zone->GetDoNotAllowVias() )
|
||||
addKeepoutConstraint( DRC_DISALLOW_VIAS );
|
||||
|
||||
if( zone->GetDoNotAllowPads() )
|
||||
addKeepoutConstraint( DRC_DISALLOW_PADS );
|
||||
|
||||
if( zone->GetDoNotAllowCopperPour() )
|
||||
addKeepoutConstraint( DRC_DISALLOW_ZONES );
|
||||
|
||||
if( zone->GetDoNotAllowFootprints() )
|
||||
addKeepoutConstraint( DRC_DISALLOW_FOOTPRINTS );
|
||||
}
|
||||
|
||||
ReportAux( wxString::Format( "Building %d implicit netclass rules",
|
||||
(int) netclassClearanceRules.size() ) );
|
||||
}
|
||||
|
@ -557,6 +619,15 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aTestTracksAgainstZones,
|
|||
m_errorLimits[ ii ] = INT_MAX;
|
||||
}
|
||||
|
||||
for( ZONE_CONTAINER* zone : m_board->Zones() )
|
||||
zone->CacheBoundingBox();
|
||||
|
||||
for( MODULE* module : m_board->Modules() )
|
||||
{
|
||||
for( ZONE_CONTAINER* zone : module->Zones() )
|
||||
zone->CacheBoundingBox();
|
||||
}
|
||||
|
||||
for( DRC_TEST_PROVIDER* provider : m_testProviders )
|
||||
{
|
||||
if( !provider->IsEnabled() )
|
||||
|
@ -677,6 +748,45 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRulesForItems( DRC_CONSTRAINT_TYPE_T aConstraintI
|
|||
c->constraint.GetName(),
|
||||
MessageTextFromValue( UNITS, clearance ) ) )
|
||||
}
|
||||
else if( aConstraintId == DRC_CONSTRAINT_TYPE_DISALLOW )
|
||||
{
|
||||
int mask;
|
||||
|
||||
if( a->GetFlags() & HOLE_PROXY )
|
||||
{
|
||||
mask = DRC_DISALLOW_HOLES;
|
||||
}
|
||||
else if( a->Type() == PCB_VIA_T )
|
||||
{
|
||||
if( static_cast<const VIA*>( a )->GetViaType() == VIATYPE::BLIND_BURIED )
|
||||
mask = DRC_DISALLOW_VIAS | DRC_DISALLOW_BB_VIAS;
|
||||
else if( static_cast<const VIA*>( a )->GetViaType() == VIATYPE::MICROVIA )
|
||||
mask = DRC_DISALLOW_VIAS | DRC_DISALLOW_MICRO_VIAS;
|
||||
else
|
||||
mask = DRC_DISALLOW_VIAS;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch( a->Type() )
|
||||
{
|
||||
case PCB_TRACE_T: mask = DRC_DISALLOW_TRACKS; break;
|
||||
case PCB_ARC_T: mask = DRC_DISALLOW_TRACKS; break;
|
||||
case PCB_PAD_T: mask = DRC_DISALLOW_PADS; break;
|
||||
case PCB_MODULE_T: mask = DRC_DISALLOW_FOOTPRINTS; break;
|
||||
case PCB_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
|
||||
case PCB_FP_SHAPE_T: mask = DRC_DISALLOW_GRAPHICS; break;
|
||||
case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
|
||||
case PCB_FP_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
|
||||
case PCB_ZONE_AREA_T: mask = DRC_DISALLOW_ZONES; break;
|
||||
case PCB_FP_ZONE_AREA_T: mask = DRC_DISALLOW_ZONES; break;
|
||||
case PCB_LOCATE_HOLE_T: mask = DRC_DISALLOW_HOLES; break;
|
||||
default: mask = 0; break;
|
||||
}
|
||||
}
|
||||
|
||||
if( ( c->constraint.m_DisallowFlags & mask ) == 0 )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
REPORT( wxString::Format( _( "Checking %s." ),
|
||||
|
|
|
@ -522,7 +522,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I
|
|||
if( zone->GetFilledPolysList( aLayer ).IsEmpty() )
|
||||
continue;
|
||||
|
||||
if( !refSegInflatedBB.Intersects( zone->GetBoundingBox() ) )
|
||||
if( !refSegInflatedBB.Intersects( zone->GetCachedBoundingBox() ) )
|
||||
continue;
|
||||
|
||||
int halfWidth = refSegWidth / 2;
|
||||
|
|
|
@ -73,29 +73,43 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
|
|||
if( !reportPhase( _( "Checking keepouts & disallow constraints..." ) ) )
|
||||
return false;
|
||||
|
||||
auto checkItem = [&]( BOARD_ITEM *item ) -> bool
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ALLOWED_ITEMS ) )
|
||||
return false;
|
||||
auto doCheckItem =
|
||||
[&]( BOARD_ITEM* item )
|
||||
{
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_DISALLOW,
|
||||
item );
|
||||
if( constraint.m_DisallowFlags )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ALLOWED_ITEMS );
|
||||
|
||||
auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_DISALLOW, item );
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " (%s)" ),
|
||||
constraint.GetName() );
|
||||
|
||||
if( constraint.m_DisallowFlags )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ALLOWED_ITEMS );
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( item );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " (%s)" ),
|
||||
constraint.GetName() );
|
||||
reportViolation( drcItem, item->GetPosition());
|
||||
}
|
||||
};
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( item );
|
||||
drcItem->SetViolatingRule( constraint.GetParentRule() );
|
||||
auto checkItem =
|
||||
[&]( BOARD_ITEM* item ) -> bool
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ALLOWED_ITEMS ) )
|
||||
return false;
|
||||
|
||||
reportViolation( drcItem, item->GetPosition());
|
||||
}
|
||||
item->ClearFlags( HOLE_PROXY );
|
||||
doCheckItem( item );
|
||||
|
||||
return true;
|
||||
};
|
||||
if( item->Type() == PCB_VIA_T || item->Type() == PCB_PAD_T )
|
||||
{
|
||||
item->SetFlags( HOLE_PROXY );
|
||||
doCheckItem( item );
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
forEachGeometryItem( {}, LSET::AllLayersMask(), checkItem );
|
||||
|
||||
|
|
|
@ -24,10 +24,9 @@
|
|||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <reporter.h>
|
||||
#include <class_board.h>
|
||||
#include <class_track.h>
|
||||
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <pcb_expr_evaluator.h>
|
||||
|
||||
#include <connectivity/connectivity_data.h>
|
||||
|
@ -35,7 +34,7 @@
|
|||
#include <connectivity/from_to_cache.h>
|
||||
|
||||
#include <drc/drc_engine.h>
|
||||
|
||||
#include <libs/kimath/include/geometry/shape_circle.h>
|
||||
|
||||
bool exprFromTo( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||
{
|
||||
|
@ -241,14 +240,35 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
|
|||
|
||||
if( zone )
|
||||
{
|
||||
SHAPE_POLY_SET testPoly;
|
||||
if( !zone->GetCachedBoundingBox().Intersects( item->GetBoundingBox() ) )
|
||||
return;
|
||||
|
||||
item->TransformShapeWithClearanceToPolygon( testPoly, context->GetLayer(), 0,
|
||||
ARC_LOW_DEF, ERROR_INSIDE );
|
||||
testPoly.BooleanIntersection( *zone->Outline(), SHAPE_POLY_SET::PM_FAST );
|
||||
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( testPoly.OutlineCount() )
|
||||
result->Set( 1.0 );
|
||||
if( zone->Outline()->Collide( holeShape ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue