drc_proto: follow up Jeff's changes in legacy DRC/board model

This commit is contained in:
Tomasz Wlostowski 2020-07-15 18:23:56 +02:00
parent 832a8c5bf7
commit 91b68e4578
7 changed files with 169 additions and 535 deletions

View File

@ -111,7 +111,7 @@ void test::DRC_ENGINE::inferImplicitRules()
}
static const int drc_debug_level = -10;
static const int drc_debug_level = 2;
void test::drc_dbg( int level, const char* fmt, ... )
{
@ -156,6 +156,7 @@ bool test::DRC_ENGINE::CompileRules()
if( rule->GetPriority() == 0 )
{
drc_dbg(1,"DefaultRule for %d = %p\n", id, rule );
m_ruleMap[ id ]->defaultRule = rule;
continue;
}
@ -306,10 +307,16 @@ std::vector<test::DRC_RULE*> test::DRC_ENGINE::QueryRulesById( test::DRC_RULE_ID
{
std::vector<test::DRC_RULE*> rv;
rv.push_back( m_ruleMap[ruleID]->defaultRule );
auto dr = m_ruleMap[ruleID]->defaultRule;
assert( dr );
if ( dr )
rv.push_back( dr );
for( auto rule : m_ruleMap[ruleID]->sortedRules )
{
assert( rule );
assert( rule->rule );
rv.push_back(rule->rule);
}

View File

@ -65,85 +65,42 @@ class DRC_CONSTRAINT;
/// DRC error codes:
enum PCB_DRC_CODE
{
DRCE_FIRST = 1,
DRCE_UNCONNECTED_ITEMS = DRCE_FIRST, ///< items are unconnected
DRCE_TRACK_NEAR_HOLE, ///< thru hole is too close to track
DRCE_TRACK_NEAR_PAD, ///< pad too close to track
DRCE_TRACK_NEAR_VIA, ///< track too close to via
DRCE_TRACK_NEAR_ZONE, ///< track & zone collide or are too close together
DRCE_TRACK_NEAR_COPPER, ///< track & copper graphic collide or are too close
DRCE_VIA_NEAR_VIA, ///< via too close to via
DRCE_VIA_NEAR_TRACK, ///< via too close to track
DRCE_VIA_NEAR_COPPER, ///< via and copper graphic collide or are too close
DRCE_TRACK_ENDS, ///< track ends are too close
DRCE_TRACK_SEGMENTS_TOO_CLOSE, ///< 2 parallel track segments too close: segm ends between segref ends
DRCE_TRACKS_CROSSING, ///< tracks are crossing
DRCE_TRACK_NEAR_EDGE, ///< track too close to board edge
DRCE_VIA_NEAR_EDGE, ///< via too close to board edge
DRCE_PAD_NEAR_EDGE, ///< pad too close to board edge
DRCE_PAD_NEAR_PAD, ///< pad too close to pad
DRCE_PAD_NEAR_COPPER, ///< pad and copper graphic collide or are too close
DRCE_ZONES_INTERSECT, ///< copper area outlines intersect
DRCE_ZONES_TOO_CLOSE, ///< copper area outlines are too close
DRCE_ZONE_HAS_EMPTY_NET, ///< copper area has a net but no pads in nets, which is suspicious
DRCE_DANGLING_VIA, ///< via which isn't connected to anything
DRCE_DANGLING_TRACK, ///< track with at least one end not connected to anything
DRCE_HOLE_NEAR_PAD, ///< hole too close to pad
DRCE_HOLE_NEAR_TRACK, ///< hole too close to track
DRCE_DRILLED_HOLES_TOO_CLOSE, ///< overlapping drilled holes break drill bits
DRCE_TOO_SMALL_TRACK_WIDTH, ///< Too small track width
DRCE_TOO_LARGE_TRACK_WIDTH, ///< Too small track width
DRCE_TOO_SMALL_VIA, ///< Too small via size
DRCE_TOO_SMALL_VIA_ANNULUS, ///< Via size and drill leave annulus too small
DRCE_TOO_SMALL_VIA_DRILL, ///< Too small via drill
DRCE_TOO_SMALL_PAD_DRILL, ///< Too small via drill
DRCE_VIA_HOLE_BIGGER, ///< via's hole is bigger than its diameter
DRCE_MICROVIA_NOT_ALLOWED, ///< micro vias are not allowed
DRCE_MICROVIA_TOO_MANY_LAYERS, ///< micro via's layer pair incorrect (layers must be adjacent)
DRCE_TOO_SMALL_MICROVIA, ///< Too small micro via size
DRCE_TOO_SMALL_MICROVIA_DRILL, ///< Too small micro via drill
DRCE_BURIED_VIA_NOT_ALLOWED, ///< buried vias are not allowed
DRCE_NETCLASS_TRACKWIDTH, ///< netclass has TrackWidth < board.m_designSettings->m_TrackMinWidth
DRCE_NETCLASS_CLEARANCE, ///< netclass has Clearance < board.m_designSettings->m_TrackClearance
DRCE_NETCLASS_VIAANNULUS, ///< netclass ViaSize & ViaDrill leave annulus < board.m_designSettings->m_ViasMinAnnulus
DRCE_NETCLASS_VIASIZE, ///< netclass has ViaSize < board.m_designSettings->m_ViasMinSize
DRCE_NETCLASS_VIADRILLSIZE, ///< netclass has ViaDrillSize < board.m_designSettings->m_MinThroughDrill
DRCE_NETCLASS_uVIASIZE, ///< netclass has ViaSize < board.m_designSettings->m_MicroViasMinSize
DRCE_NETCLASS_uVIADRILLSIZE, ///< netclass has ViaSize < board.m_designSettings->m_MicroViasMinDrill
DRCE_VIA_INSIDE_KEEPOUT,
DRCE_MICROVIA_INSIDE_KEEPOUT,
DRCE_BBVIA_INSIDE_KEEPOUT,
DRCE_TRACK_INSIDE_KEEPOUT,
DRCE_PAD_INSIDE_KEEPOUT,
DRCE_FOOTPRINT_INSIDE_KEEPOUT,
DRCE_HOLE_INSIDE_KEEPOUT,
DRCE_TEXT_INSIDE_KEEPOUT,
DRCE_GRAPHICS_INSIDE_KEEPOUT,
DRCE_OVERLAPPING_FOOTPRINTS, ///< footprint courtyards overlap
DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined
DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes)
DRCE_FIRST = 1,
DRCE_UNCONNECTED_ITEMS = DRCE_FIRST, ///< items are unconnected
DRCE_ALLOWED_ITEMS, ///< a disallowed item has been used
DRCE_CLEARANCE, ///< items are too close together
DRCE_HOLE_CLEARANCE,
DRCE_TRACKS_CROSSING, ///< tracks are crossing
DRCE_COPPER_EDGE_CLEARANCE, ///< a copper item is too close to the board edge
DRCE_ZONES_INTERSECT, ///< copper area outlines intersect
DRCE_ZONE_HAS_EMPTY_NET, ///< copper area has a net but no pads in nets, which is suspicious
DRCE_DANGLING_VIA, ///< via which isn't connected to anything
DRCE_DANGLING_TRACK, ///< track with at least one end not connected to anything
DRCE_DRILLED_HOLES_TOO_CLOSE, ///< overlapping drilled holes break drill bits
DRCE_TRACK_WIDTH, ///< Track width is too small or too large
DRCE_TOO_SMALL_VIA, ///< Too small via size
DRCE_VIA_ANNULUS, ///< Via size and drill leave annulus too small or too large
DRCE_TOO_SMALL_DRILL, ///< Too small via or pad drill
DRCE_VIA_HOLE_BIGGER, ///< via's hole is bigger than its diameter
DRCE_PADSTACK, ///< something is wrong with a pad or via stackup
DRCE_TOO_SMALL_MICROVIA, ///< Too small micro via size
DRCE_TOO_SMALL_MICROVIA_DRILL, ///< Too small micro via drill
DRCE_KEEPOUT, ///< A disallowed object is inside a keepout
DRCE_OVERLAPPING_FOOTPRINTS, ///< footprint courtyards overlap
DRCE_MISSING_COURTYARD, ///< footprint has no courtyard defined
DRCE_MALFORMED_COURTYARD, ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes)
DRCE_PTH_IN_COURTYARD,
DRCE_NPTH_IN_COURTYARD,
DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
DRCE_INVALID_OUTLINE, ///< invalid board outline
DRCE_MISSING_FOOTPRINT, ///< footprint not found for netlist item
DRCE_DUPLICATE_FOOTPRINT, ///< more than one footprints found for netlist item
DRCE_EXTRA_FOOTPRINT, ///< netlist item not found for footprint
DRCE_DISABLED_LAYER_ITEM, ///< item on a disabled layer
DRCE_INVALID_OUTLINE, ///< invalid board outline
DRCE_MISSING_FOOTPRINT, ///< footprint not found for netlist item
DRCE_DUPLICATE_FOOTPRINT, ///< more than one footprints found for netlist item
DRCE_EXTRA_FOOTPRINT, ///< netlist item not found for footprint
DRCE_UNRESOLVED_VARIABLE,
DRCE_LAST = DRCE_UNRESOLVED_VARIABLE,
// These are actually Cleanup Tracks and Vias actions, not DRCE errors
CLEANUP_SHORT,
CLEANUP_REDUNDANT_VIA,
CLEANUP_DUPLICATE_TRACK,
CLEANUP_MERGE_TRACKS,
CLEANUP_DANGLING_TRACK,
CLEANUP_DANGLING_VIA,
CLEANUP_ZERO_LENGTH_TRACK,
CLEANUP_TRACK_IN_PAD
DRCE_LAST = DRCE_UNRESOLVED_VARIABLE
};

View File

@ -28,6 +28,8 @@
#include <drc_proto/drc_rule.h>
#include <drc_proto/drc_rule_parser.h>
#include <drc_proto/drc_rules_lexer.h>
#include <drc_proto/drc_engine.h> // drc_dbg
#include <fctsys.h>
@ -61,8 +63,6 @@ void test::DRC_RULES_PARSER::Parse(
token = NextTok();
printf("TOK %d %d\n", token, T_version );
if( !haveVersion && token != T_version )
Expecting( "version" );
@ -81,35 +81,17 @@ void test::DRC_RULES_PARSER::Parse(
break;
case T_rule:
aRules.push_back( parseRULE() );
{
auto rule = parseRULE();
drc_dbg(0, "Parsed rule: '%s' type '%s\n", (const char*) rule->GetName().c_str(), (const char*) rule->GetTestProviderName().c_str() );
aRules.push_back( rule );
break;
}
default:
Expecting( "condition or rule" );
}
}
#if 0
// Hook up the selectors to their rules
std::map<wxString, DRC_RULE*> ruleMap;
for( DRC_RULE* rule : aRules )
ruleMap[ rule->m_Name ] = rule;
for( const std::pair<DRC_RULE_CONDITION*, wxString>& entry : conditionsRules )
{
if( ruleMap.count( entry.second ) )
{
entry.first->m_Rule = ruleMap[ entry.second ];
}
else
{
wxString errText = wxString::Format( _( "Rule \"%s\" not found." ), entry.second );
THROW_PARSE_ERROR( errText, CurSource(), "", 0, 0 );
}
}
#endif
}

View File

@ -94,211 +94,3 @@ wxPoint test::DRC_TEST_PROVIDER_CLEARANCE_BASE::getLocation( TRACK* aTrack, cons
return pt1;
}
/*
* Test if distance between a segment and a pad is > minClearance. Return the actual
* distance if it is less.
*/
bool test::DRC_TEST_PROVIDER_CLEARANCE_BASE::checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad,
int minClearance, int* aActualDist )
{
if( ( pad->GetShape() == PAD_SHAPE_CIRCLE || pad->GetShape() == PAD_SHAPE_OVAL ) )
{
/* Treat an oval pad as a line segment along the hole's major axis,
* shortened by half its minor axis.
* A circular pad is just a degenerate case of an oval hole.
*/
wxPoint padStart, padEnd;
int padWidth;
pad->GetOblongGeometry( pad->GetSize(), &padStart, &padEnd, &padWidth );
padStart += pad->ShapePos();
padEnd += pad->ShapePos();
SEG padSeg( padStart, padEnd );
int widths = ( padWidth + refSegWidth ) / 2;
int center2centerAllowed = minClearance + widths;
// Avoid square-roots if possible (for performance)
SEG::ecoord center2center_squared = refSeg.SquaredDistance( padSeg );
if( center2center_squared < SEG::Square( center2centerAllowed ) )
{
*aActualDist = std::max( 0.0, sqrt( center2center_squared ) - widths );
return false;
}
}
else if( ( pad->GetShape() == PAD_SHAPE_RECT || pad->GetShape() == PAD_SHAPE_ROUNDRECT )
&& ( (int) pad->GetOrientation() % 900 == 0 ) )
{
EDA_RECT padBBox = pad->GetBoundingBox();
int widths = refSegWidth / 2;
// Note a ROUNDRECT pad with a corner radius = r can be treated as a smaller
// RECT (size - 2*r) with a clearance increased by r
if( pad->GetShape() == PAD_SHAPE_ROUNDRECT )
{
padBBox.Inflate( - pad->GetRoundRectCornerRadius() );
widths += pad->GetRoundRectCornerRadius();
}
SHAPE_RECT padShape( padBBox.GetPosition(), padBBox.GetWidth(), padBBox.GetHeight() );
int actual;
if( padShape.Collide( refSeg, minClearance + widths, &actual ) )
{
*aActualDist = std::max( 0, actual - widths );
return false;
}
}
else // Convert the rest to polygons
{
SHAPE_POLY_SET polyset;
BOARD* board = pad->GetBoard();
int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF;
pad->TransformShapeWithClearanceToPolygon( polyset, 0, maxError );
const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
int widths = refSegWidth / 2;
int actual;
if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
(wxPoint) refSeg.A, (wxPoint) refSeg.B,
minClearance + widths, &actual ) )
{
*aActualDist = std::max( 0, actual - widths );
return false;
}
}
return true;
}
bool test::DRC_TEST_PROVIDER_CLEARANCE_BASE::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual )
{
int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) );
// Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return true;
int actual = INT_MAX;
for( const std::shared_ptr<SHAPE>& aShape : aRefPad->GetEffectiveShapes() )
{
for( const std::shared_ptr<SHAPE>& bShape : aPad->GetEffectiveShapes() )
{
int this_dist;
if( aShape->Collide( bShape.get(), aMinClearance, &this_dist ) )
actual = std::min( actual, this_dist );
}
}
if( actual < INT_MAX )
{
// returns the actual clearance (clearance < aMinClearance) for diags:
if( aActual )
*aActual = std::max( 0, actual );
return false;
}
return true;
}
bool test::DRC_TEST_PROVIDER_CLEARANCE_BASE::poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd,
int aDist, int* aActual )
{
/* Test if the segment is contained in the polygon.
* This case is not covered by the following check if the segment is
* completely contained in the polygon (because edges don't intersect)!
*/
if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
{
*aActual = 0;
return false;
}
for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
{ // for all edges in polygon
double d;
if( TestForIntersectionOfStraightLineSegments( aTref[ii].x, aTref[ii].y, aTref[jj].x,
aTref[jj].y, aSegStart.x, aSegStart.y,
aSegEnd.x, aSegEnd.y, NULL, NULL, &d ) )
{
*aActual = 0;
return false;
}
if( d < aDist )
{
*aActual = KiROUND( d );
return false;
}
}
return true;
}
#if 0
/**
* compare 2 convex polygons and return true if distance > aDist (if no error DRC)
* i.e if for each edge of the first polygon distance from each edge of the other polygon
* is >= aDist
*/
bool test::DRC_TEST_PROVIDER_CLEARANCE_BASE::poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTtest, int aTtestCount,
int aAllowedDist, int* actualDist )
{
/* Test if one polygon is contained in the other and thus the polygon overlap.
* This case is not covered by the following check if one polygone is
* completely contained in the other (because edges don't intersect)!
*/
if( TestPointInsidePolygon( aTref, aTrefCount, aTtest[0] ) )
{
*actualDist = 0;
return false;
}
if( TestPointInsidePolygon( aTtest, aTtestCount, aTref[0] ) )
{
*actualDist = 0;
return false;
}
for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ )
{
// for all edges in aTref
for( int kk = 0, ll = aTtestCount - 1; kk < aTtestCount; ll = kk, kk++ )
{
// for all edges in aTtest
double d;
int intersect = TestForIntersectionOfStraightLineSegments(
aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
aTtest[kk].x, aTtest[kk].y, aTtest[ll].x, aTtest[ll].y,
nullptr, nullptr, &d );
if( intersect )
{
*actualDist = 0;
return false;
}
if( d < aAllowedDist )
{
*actualDist = KiROUND( d );
return false;
}
}
}
return true;
}
#endif

View File

@ -52,11 +52,11 @@ protected:
bool doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_limit );
bool checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad,
int minClearance, int* aActualDist );
bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual );
bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd,
int aDist, int* aActual );
//bool checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad,
// int minClearance, int* aActualDist );
//bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual );
//bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd,
// int aDist, int* aActual );
//bool poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTtest, int aTtestCount,
// int aAllowedDist, int* actualDist );

View File

@ -9,6 +9,7 @@
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h>
#include <drc_proto/drc_engine.h>
#include <drc_proto/drc_item.h>
@ -66,8 +67,12 @@ bool test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run()
for( auto rule : m_drcEngine->QueryRulesById( test::DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE ) )
{
drc_dbg(1, "process rule %p\n", rule );
if( rule->GetConstraint().m_Value.HasMin() )
{
m_largestClearance = std::max( m_largestClearance, rule->GetConstraint().m_Value.Min() );
drc_dbg(1, "min-copper-clearance %d\n", rule->GetConstraint().m_Value.Min() );
}
}
ReportAux( "Worst clearance : %d nm", m_largestClearance );
@ -126,88 +131,20 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperTextAndGraphics()
void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem )
{
EDA_RECT bbox;
std::vector<SEG> itemShape;
int itemWidth;
DRAWSEGMENT* drawItem = dynamic_cast<DRAWSEGMENT*>( aItem );
EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aItem );
EDA_RECT bbox;
std::shared_ptr<SHAPE> itemShape;
DRAWSEGMENT* drawItem = dynamic_cast<DRAWSEGMENT*>( aItem );
EDA_TEXT* textItem = dynamic_cast<EDA_TEXT*>( aItem );
if( drawItem )
{
bbox = drawItem->GetBoundingBox();
itemWidth = drawItem->GetWidth();
switch( drawItem->GetShape() )
{
case S_ARC:
{
SHAPE_ARC arc( drawItem->GetCenter(), drawItem->GetArcStart(),
(double) drawItem->GetAngle() / 10.0 );
SHAPE_LINE_CHAIN l = arc.ConvertToPolyline();
for( int i = 0; i < l.SegmentCount(); i++ )
itemShape.push_back( l.Segment( i ) );
break;
}
case S_SEGMENT:
itemShape.emplace_back( SEG( drawItem->GetStart(), drawItem->GetEnd() ) );
break;
case S_CIRCLE:
{
// SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC
SHAPE_ARC circle( drawItem->GetCenter(), drawItem->GetEnd(), 360.0 );
SHAPE_LINE_CHAIN l = circle.ConvertToPolyline();
for( int i = 0; i < l.SegmentCount(); i++ )
itemShape.push_back( l.Segment( i ) );
break;
}
case S_CURVE:
{
drawItem->RebuildBezierToSegmentsPointsList( drawItem->GetWidth() );
wxPoint start_pt = drawItem->GetBezierPoints()[0];
for( unsigned int jj = 1; jj < drawItem->GetBezierPoints().size(); jj++ )
{
wxPoint end_pt = drawItem->GetBezierPoints()[jj];
itemShape.emplace_back( SEG( start_pt, end_pt ) );
start_pt = end_pt;
}
break;
}
case S_POLYGON:
{
SHAPE_LINE_CHAIN l = drawItem->GetPolyShape().Outline( 0 );
for( int i = 0; i < l.SegmentCount(); i++ )
itemShape.push_back( l.Segment( i ) );
}
break;
default:
wxFAIL_MSG( "unknown shape type" );
break;
}
itemShape = drawItem->GetEffectiveShape();
}
else if( textItem )
else if( textItem )
{
bbox = textItem->GetTextBox();
itemWidth = textItem->GetEffectiveTextPenWidth();
std::vector<wxPoint> textShape;
textItem->TransformTextShapeToSegmentList( textShape );
for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
itemShape.emplace_back( SEG( textShape[jj], textShape[jj+1] ) );
itemShape = textItem->GetEffectiveShape();
}
else
{
@ -215,10 +152,10 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* a
return;
}
SHAPE_RECT rect_area( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() );
SHAPE_RECT bboxShape( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() );
if( itemShape.empty() )
return;
//if( itemShape->Empty() )
// return;
// Test tracks and vias
for( auto track : m_board->Tracks() )
@ -228,34 +165,24 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* a
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE, aItem, track );
auto minClearance = rule->GetConstraint().GetValue().Min();
int widths = ( track->GetWidth() + itemWidth ) / 2;
int center2centerAllowed = minClearance + widths;
int actual = INT_MAX;
wxPoint pos;
SEG trackSeg( track->GetStart(), track->GetEnd() );
SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() );
// Fast test to detect a track segment candidate inside the text bounding box
if( !rect_area.Collide( trackSeg, center2centerAllowed ) )
if( !bboxShape.Collide( &trackSeg, 0 ) )
continue;
OPT<SEG> minSeg;
SEG::ecoord center2center_squared = 0;
if( !itemShape->Collide( &trackSeg, minClearance, &actual ) )
continue;
for( const SEG& itemSeg : itemShape )
pos = (wxPoint) itemShape->Centre();
if( actual < INT_MAX )
{
SEG::ecoord thisDist_squared = trackSeg.SquaredDistance( itemSeg );
int errorCode = DRCE_CLEARANCE;
if( !minSeg || thisDist_squared < center2center_squared )
{
minSeg = itemSeg;
center2center_squared = thisDist_squared;
}
}
if( center2center_squared < SEG::Square( center2centerAllowed ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
int errorCode = ( track->Type() == PCB_VIA_T ) ? DRCE_VIA_NEAR_COPPER
: DRCE_TRACK_NEAR_COPPER;
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
@ -267,7 +194,6 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* a
drcItem->SetItems( track, aItem );
drcItem->SetViolatingRule( rule );
wxPoint pos = getLocation( track, minSeg.get() );
ReportWithMarker( drcItem, pos );
}
}
@ -285,36 +211,21 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* a
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE, aItem, pad );
auto minClearance = rule->GetConstraint().GetValue().Min();
int widths = itemWidth / 2;
int center2centerAllowed = minClearance + widths;
int actual = INT_MAX;
int bb_radius = pad->GetBoundingRadius() + minClearance;
// Fast test to detect a pad candidate inside the text bounding box
// Finer test (time consumming) is made only for pads near the text.
int bb_radius = pad->GetBoundingRadius() + minClearance;
VECTOR2I shape_pos( pad->ShapePos() );
if( !rect_area.Collide( SEG( shape_pos, shape_pos ), bb_radius ) )
if( !bboxShape.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) )
continue;
const std::shared_ptr<SHAPE_POLY_SET>& padOutline = pad->GetEffectivePolygon();
OPT<SEG> minSeg;
SEG::ecoord center2center_squared = 0;
if( !pad->GetEffectiveShape()->Collide( itemShape.get(), minClearance, &actual ) )
continue;
for( const SEG& itemSeg : itemShape )
if( actual < INT_MAX )
{
SEG::ecoord thisCenter2center_squared = padOutline.SquaredDistance( itemSeg );
if( !minSeg || thisCenter2center_squared < center2center_squared )
{
minSeg = itemSeg;
center2center_squared = thisCenter2center_squared;
}
}
if( center2center_squared < SEG::Square( center2centerAllowed ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_PAD_NEAR_COPPER );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_CLEARANCE );
wxString msg;
@ -366,7 +277,7 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
{
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
SHAPE_SEGMENT refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd(), aRefSeg->GetWidth() );
PCB_LAYER_ID refLayer = aRefSeg->GetLayer();
LSET refLayerSet = aRefSeg->GetLayerSet();
@ -414,11 +325,9 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
int clearanceAllowed = minClearance - bds.GetDRCEpsilon();
int actual;
if( !checkClearanceSegmToPad( refSeg, refSegWidth, pad, clearanceAllowed, &actual ) )
if( pad->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual ) )
{
actual = std::max( 0, actual );
SEG padSeg( pad->GetPosition(), pad->GetPosition() );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_PAD );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_CLEARANCE );
wxString msg;
@ -431,9 +340,9 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
drcItem->SetItems( aRefSeg, pad );
drcItem->SetViolatingRule( rule );
ReportWithMarker( drcItem, getLocation( aRefSeg, padSeg ) );
ReportWithMarker( drcItem, pad->GetPosition() );
if( isErrorLimitExceeded( DRCE_TRACK_NEAR_PAD ) )
if( isErrorLimitExceeded( DRCE_CLEARANCE ) )
return;
}
}
@ -485,16 +394,10 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE, aRefSeg, track );
auto minClearance = rule->GetConstraint().GetValue().Min();
SEG trackSeg( track->GetStart(), track->GetEnd() );
int widths = ( refSegWidth + track->GetWidth() ) / 2;
int center2centerAllowed = minClearance + widths;
// Avoid square-roots if possible (for performance)
SEG::ecoord center2center_squared = refSeg.SquaredDistance( trackSeg );
OPT_VECTOR2I intersection = refSeg.Intersect( trackSeg );
// Check two tracks crossing first as it reports a DRCE without distances
if( intersection )
SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() );
int actual;
if( OPT_VECTOR2I intersection = refSeg.GetSeg().Intersect( trackSeg.GetSeg() ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACKS_CROSSING );
@ -508,18 +411,11 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
if( isErrorLimitExceeded( DRCE_TRACKS_CROSSING ) )
return;
}
else if( center2center_squared < SEG::Square( center2centerAllowed ) )
else if( refSeg.Collide( &trackSeg, minClearance, &actual ) )
{
int errorCode = DRCE_TRACK_ENDS;
wxPoint pos = getLocation( aRefSeg, trackSeg.GetSeg() );
int errorCode = DRCE_CLEARANCE;
if( aRefSeg->Type() == PCB_VIA_T && track->Type() == PCB_VIA_T )
errorCode = DRCE_VIA_NEAR_VIA;
else if( aRefSeg->Type() == PCB_VIA_T || track->Type() == PCB_VIA_T )
errorCode = DRCE_VIA_NEAR_TRACK;
else if( refSeg.ApproxParallel( trackSeg ) )
errorCode = DRCE_TRACK_SEGMENTS_TOO_CLOSE;
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( errorCode );
wxString msg;
@ -532,7 +428,7 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
drcItem->SetItems( aRefSeg, track );
drcItem->SetViolatingRule( rule );
ReportWithMarker( drcItem, getLocation( aRefSeg, trackSeg ) );
ReportWithMarker( drcItem, pos );
if( isErrorLimitExceeded( errorCode ) )
return;
@ -564,38 +460,35 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, TRACK
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_CLEARANCE, aRefSeg, zone );
auto minClearance = rule->GetConstraint().GetValue().Min();
int widths = refSegWidth / 2;
int widths = refSegWidth / 2;
int center2centerAllowed = minClearance + widths;
SHAPE_POLY_SET* outline =
const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList( layer ) );
// to avoid false positive, due to rounding issues and approxiamtions
// in distance and clearance calculations, use a small threshold for distance
// (1 micron)
#define THRESHOLD_DIST Millimeter2iu( 0.001 )
SEG::ecoord center2center_squared = outline->SquaredDistance( testSeg );
int allowedDist = minClearance + widths + THRESHOLD_DIST;
int actual = INT_MAX;
// to avoid false positive, due to rounding issues and approxiamtions
// in distance and clearance calculations, use a small threshold for distance
// (1 micron)
#define THRESHOLD_DIST Millimeter2iu( 0.001 )
if( zone->GetFilledPolysList( layer ).Collide( testSeg, allowedDist, &actual ) )
{
actual = std::max( 0, actual - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_CLEARANCE );
wxString msg;
if( center2center_squared + THRESHOLD_DIST < SEG::Square( center2centerAllowed ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_ZONE );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
rule->GetName(),
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
rule->GetName(),
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( aRefSeg, zone );
drcItem->SetViolatingRule( rule );
drcItem->SetErrorMessage( msg );
drcItem->SetItems( aRefSeg, zone );
drcItem->SetViolatingRule( rule );
ReportWithMarker( drcItem, getLocation( aRefSeg, zone ) );
ReportWithMarker( drcItem, getLocation( aRefSeg, zone ) );
}
}
}
}
}
// fixme: board edge clearance to another rule
@ -656,16 +549,6 @@ bool test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doPadToPadsDrc( D_PAD* aRefPad, D
LSET layerMask = aRefPad->GetLayerSet() & all_cu;
// For hole testing we use a dummy pad which is given the shape of the hole. Note that
// this pad must have a parent because some functions expect a non-null parent to find
// the pad's board.
MODULE dummymodule( m_drcEngine->GetBoard() ); // Creates a dummy parent
D_PAD dummypad( &dummymodule );
// Ensure the hole is on all copper layers
dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );
for( D_PAD** pad_list = aStart; pad_list<aEnd; ++pad_list )
{
D_PAD* pad = *pad_list;
@ -710,9 +593,9 @@ bool test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::doPadToPadsDrc( D_PAD* aRefPad, D
int clearanceAllowed = minClearance - m_drcEngine->GetDesignSettings()->GetDRCEpsilon();
int actual;
if( !checkClearancePadToPad( aRefPad, pad, clearanceAllowed, &actual ) )
if( aRefPad->Collide( pad, clearanceAllowed, &actual ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_PAD_NEAR_PAD );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_CLEARANCE );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
/*m_clearanceSource fixme*/ "",
@ -880,7 +763,7 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZones()
}
else
{
drcItem = new DRC_ITEM( DRCE_ZONES_TOO_CLOSE );
drcItem = new DRC_ITEM( DRCE_CLEARANCE );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
@ -911,4 +794,4 @@ std::set<test::DRC_RULE_ID_T> test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::GetMatch
namespace detail
{
static test::DRC_REGISTER_TEST_PROVIDER<test::DRC_TEST_PROVIDER_COPPER_CLEARANCE> dummy;
}
}

View File

@ -44,9 +44,28 @@ public:
virtual std::set<test::DRC_RULE_ID_T> GetMatchingRuleIds() const override;
private:
bool checkPad( D_PAD* aPad );
bool checkVia( VIA* aVia );
bool checkMicroVia( VIA* aVia );
void addHole( const wxPoint& aLocation, int aRadius, BOARD_ITEM* aOwner );
bool checkHoles();
void testPadHoles();
bool doPadToPadHoleDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_limit );
struct DRILLED_HOLE
{
wxPoint m_location;
int m_drillRadius = 0;
BOARD_ITEM* m_owner = nullptr;
};
EDA_UNITS m_units;
BOARD* m_board;
std::vector<DRILLED_HOLE> m_holes;
int m_largestRadius;
};
};
@ -61,11 +80,15 @@ bool test::DRC_TEST_PROVIDER_HOLE_CLEARANCE::Run()
for( auto rule : m_drcEngine->QueryRulesById( test::DRC_RULE_ID_T::DRC_RULE_ID_HOLE_CLEARANCE ) )
{
drc_dbg(1, "process rule %p\n", rule );
if( rule->GetConstraint().m_Value.HasMin() )
{
drc_dbg(1, "min-hole-clearance %d\n", rule->GetConstraint().m_Value.Min() );
m_largestClearance = std::max( m_largestClearance, rule->GetConstraint().m_Value.Min() );
}
}
ReportAux( "Worst clearance : %d nm", m_largestClearance );
ReportAux( "Worst hole clearance : %d nm", m_largestClearance );
ReportStage( ("Testing pad/hole clearances"), 0, 2 );
testPadHoles();
@ -105,7 +128,7 @@ void test::DRC_TEST_PROVIDER_HOLE_CLEARANCE::testPadHoles()
for( auto& pad : sortedPads )
{
int x_limit = pad->GetPosition().x + pad->GetBoundingRadius() + max_size;
drc_dbg(4,"-> %p\n", pad);
drc_dbg(10,"-> %p\n", pad);
doPadToPadHoleDrc( pad, &pad, listEnd, x_limit );
}
}
@ -118,14 +141,6 @@ bool test::DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( D_PAD* aRefPad,
LSET layerMask = aRefPad->GetLayerSet() & all_cu;
// For hole testing we use a dummy pad which is given the shape of the hole. Note that
// this pad must have a parent because some functions expect a non-null parent to find
// the pad's board.
MODULE dummymodule( m_board ); // Creates a dummy parent
D_PAD dummypad( &dummymodule );
// Ensure the hole is on all copper layers
dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );
for( D_PAD** pad_list = aStart; pad_list<aEnd; ++pad_list )
{
@ -134,24 +149,27 @@ bool test::DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( D_PAD* aRefPad,
if( pad == aRefPad )
continue;
drc_dbg(4," chk against -> %p\n", pad);
// drc_dbg(10," chk against -> %p\n", pad);
// We can stop the test when pad->GetPosition().x > x_limit
// because the list is sorted by X values
if( pad->GetPosition().x > x_limit )
break;
drc_dbg(4," chk2 against -> %p ds %d %d\n", pad, pad->GetDrillSize().x, aRefPad->GetDrillSize().x );
// drc_dbg(10," chk2 against -> %p ds %d %d\n", pad, pad->GetDrillSize().x, aRefPad->GetDrillSize().x );
drc_dbg(1," chk1 against -> %p x0 %d x2 %d\n", pad, pad->GetDrillSize().x, aRefPad->GetDrillSize().x );
// No problem if pads which are on copper layers are on different copper layers,
// (pads can be only on a technical layer, to build complex pads)
// but their hole (if any ) can create DRC error because they are on all
// copper layers, so we test them
if( ( pad->GetLayerSet() & layerMask ) == 0 &&
if( ( pad->GetLayerSet() & layerMask ) != 0 &&
( pad->GetLayerSet() & all_cu ) != 0 &&
( aRefPad->GetLayerSet() & all_cu ) != 0 )
{
drc_dbg(4," chk3 against -> %p\n", pad);
// if holes are in the same location and have the same size and shape,
// this can be accepted
@ -167,26 +185,25 @@ bool test::DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( D_PAD* aRefPad,
continue;
}
drc_dbg(1," chk3 against -> %p x0 %d x2 %d\n", pad, pad->GetDrillSize().x, aRefPad->GetDrillSize().x );
/* Here, we must test clearance between holes and pads
* dummy pad size and shape is adjusted to pad drill size and shape
* pad size and shape is adjusted to pad drill size and shape
*/
if( pad->GetDrillSize().x )
{
drc_dbg(4,"check pad %p\n", pad );
// pad under testing has a hole, test this hole against pad reference
dummypad.SetPosition( pad->GetPosition() );
dummypad.SetSize( pad->GetDrillSize() );
dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
dummypad.SetOrientation( pad->GetOrientation() );
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_HOLE_CLEARANCE, aRefPad, &dummypad );
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_HOLE_CLEARANCE, aRefPad, pad );
auto minClearance = rule->GetConstraint().GetValue().Min();
int actual;
if( !checkClearancePadToPad( aRefPad, &dummypad, minClearance, &actual ) )
drc_dbg(1,"check pad %p rule '%s' cl %d\n", pad, (const char*) rule->GetName().c_str(), minClearance );
// fixme: pad stacks...
if( aRefPad->Collide( pad->GetEffectiveHoleShape(), minClearance, &actual ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_NEAR_PAD );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_CLEARANCE );
wxString msg;
@ -206,20 +223,16 @@ bool test::DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( D_PAD* aRefPad,
if( aRefPad->GetDrillSize().x ) // pad reference has a hole
{
drc_dbg(4,"check refpad %p\n", pad );
dummypad.SetPosition( aRefPad->GetPosition() );
dummypad.SetSize( aRefPad->GetDrillSize() );
dummypad.SetShape( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
dummypad.SetOrientation( aRefPad->GetOrientation() );
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_HOLE_CLEARANCE, aRefPad, &dummypad );
auto rule = m_drcEngine->EvalRulesForItems( test::DRC_RULE_ID_T::DRC_RULE_ID_HOLE_CLEARANCE, aRefPad, pad );
auto minClearance = rule->GetConstraint().GetValue().Min();
int actual;
if( !checkClearancePadToPad( pad, &dummypad, minClearance, &actual ) )
drc_dbg(1,"check pad %p rule '%s' cl %d\n", aRefPad, (const char*) rule->GetName().c_str(), minClearance );
if( pad->Collide( aRefPad->GetEffectiveHoleShape(), minClearance, &actual ) )
{
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_NEAR_PAD );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_CLEARANCE );
wxString msg;
msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),