Move rest of DRC to SHAPE collision architecture.

This commit is contained in:
Jeff Young 2020-07-04 19:48:22 +01:00
parent 2a1550d1d2
commit d85a707385
12 changed files with 184 additions and 492 deletions

View File

@ -430,7 +430,7 @@ COBJECT2D *BOARD_ADAPTER::createNewPadDrill( const D_PAD* aPad, int aInflateValu
}
else // Oblong hole
{
const std::shared_ptr<SHAPE_SEGMENT>& seg = aPad->GetEffectiveHoleShape();
const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
float width = seg->GetWidth() + aInflateValue * 2;
SFVEC2F start3DU( seg->GetSeg().A.x * m_biuTo3Dunits,

View File

@ -90,6 +90,16 @@ public:
return VECTOR2I( m_w, m_h ).EuclideanNorm();
}
bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const override
{
return SHAPE::Collide( aShape, aClearance, aMTV );
}
bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override
{
return SHAPE::Collide( aShape, aClearance, aActual );
}
/// @copydoc SHAPE::Collide()
bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override;

View File

@ -54,6 +54,16 @@ public:
return BOX2I( m_seg.A, m_seg.B - m_seg.A ).Inflate( aClearance + ( m_width + 1 ) / 2 );
}
bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const override
{
return SHAPE::Collide( aShape, aClearance, aMTV );
}
bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override
{
return SHAPE::Collide( aShape, aClearance, aActual );
}
bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override
{
int min_dist = ( m_width + 1 ) / 2 + aClearance;

View File

@ -651,7 +651,7 @@ bool D_PAD::TransformHoleWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
if( !drillsize.x || !drillsize.y )
return false;
const std::shared_ptr<SHAPE_SEGMENT>& seg = GetEffectiveHoleShape();
const SHAPE_SEGMENT* seg = GetEffectiveHoleShape();
TransformSegmentToPolygon( aCornerBuffer, (wxPoint) seg->GetSeg().A, (wxPoint) seg->GetSeg().B,
aError, seg->GetWidth() + aInflateValue * 2 );

View File

@ -200,12 +200,12 @@ const std::vector<std::shared_ptr<SHAPE>>& D_PAD::GetEffectiveShapes() const
}
const std::shared_ptr<SHAPE_SEGMENT>& D_PAD::GetEffectiveHoleShape() const
const SHAPE_SEGMENT* D_PAD::GetEffectiveHoleShape() const
{
if( m_shapesDirty )
BuildEffectiveShapes();
return m_effectiveHoleShape;
return m_effectiveHoleShape.get();
}
@ -761,39 +761,6 @@ void D_PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>
}
void D_PAD::GetOblongGeometry( const wxSize& aDrillOrPadSize,
wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const
{
// calculates the start point, end point and width
// of an equivalent segment which have the same position and width as the pad or hole
int delta_cx, delta_cy;
wxSize halfsize = aDrillOrPadSize / 2;
wxPoint offset;
if( aDrillOrPadSize.x > aDrillOrPadSize.y ) // horizontal
{
delta_cx = halfsize.x - halfsize.y;
delta_cy = 0;
*aWidth = aDrillOrPadSize.y;
}
else // vertical
{
delta_cx = 0;
delta_cy = halfsize.y - halfsize.x;
*aWidth = aDrillOrPadSize.x;
}
RotatePoint( &delta_cx, &delta_cy, m_Orient );
aStartPoint->x = delta_cx + offset.x;
aStartPoint->y = delta_cy + offset.y;
aEndPoint->x = - delta_cx + offset.x;
aEndPoint->y = - delta_cy + offset.y;
}
bool D_PAD::HitTest( const wxPoint& aPosition, int aAccuracy ) const
{
VECTOR2I delta = aPosition - GetPosition();
@ -846,6 +813,64 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
}
bool D_PAD::Collide( const D_PAD* aPad, int aMinClearance, int* aActual )
{
int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - ShapePos() ) );
// Quick test: Clearance is OK if the bounding circles are further away than aMinClearance
if( center2center - GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return false;
int actual = INT_MAX;
for( const std::shared_ptr<SHAPE>& aShape : 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 true;
}
return false;
}
bool D_PAD::Collide( const SHAPE_SEGMENT* aSeg, int aMinClearance, int* aActual )
{
int actual = INT_MAX;
for( const std::shared_ptr<SHAPE>& shape : GetEffectiveShapes() )
{
int this_dist;
if( shape->Collide( aSeg, aMinClearance, &this_dist ) )
actual = std::min( actual, this_dist );
}
if( actual < INT_MAX )
{
if( aActual )
*aActual = std::max( 0, actual );
return true;
}
return false;
}
int D_PAD::Compare( const D_PAD* padref, const D_PAD* padcmp )
{
int diff;

View File

@ -321,12 +321,6 @@ public:
bool IsDirty() const { return m_shapesDirty; }
/**
* JEY TODO: temporary until Tom is done with DRC stuff....
*/
void GetOblongGeometry( const wxSize& aDrillOrPadSize,
wxPoint* aStartPoint, wxPoint* aEndPoint, int* aWidth ) const;
void SetLayerSet( LSET aLayerMask ) { m_layerMask = aLayerMask; }
LSET GetLayerSet() const override { return m_layerMask; }
@ -394,7 +388,7 @@ public:
* Function GetEffectiveHoleShape
* Returns a list of SHAPE objects representing the pad's hole.
*/
const std::shared_ptr<SHAPE_SEGMENT>& GetEffectiveHoleShape() const;
const SHAPE_SEGMENT* GetEffectiveHoleShape() const;
/**
* Function GetBoundingRadius
@ -513,6 +507,9 @@ public:
bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override;
bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override;
bool Collide( const SHAPE_SEGMENT* aSeg, int aMinClearance, int* aActual = nullptr );
bool Collide( const D_PAD* aPad, int aMinClearance, int* aActual = nullptr );
wxString GetClass() const override
{
return wxT( "PAD" );

View File

@ -49,6 +49,7 @@
#include <dialog_drc.h>
#include <wx/progdlg.h>
#include <board_commit.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_arc.h>
#include <drc/drc.h>
#include <drc/drc_rule_parser.h>
@ -61,6 +62,7 @@
#include <drc/footprint_tester.h>
#include <dialogs/panel_setup_rules.h>
DRC::DRC() :
PCB_TOOL_BASE( "pcbnew.DRCTool" ),
m_editFrame( nullptr ),
@ -538,11 +540,11 @@ void DRC::testPadClearances( BOARD_COMMIT& aCommit )
for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
{
SHAPE_SEGMENT edge( *it );
int actual;
if( !checkClearanceSegmToPad( *it, 0, pad, minClearance, &actual ) )
if( pad->Collide( &edge, minClearance, &actual ) )
{
actual = std::max( 0, actual );
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_COPPER_EDGE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
@ -906,101 +908,26 @@ void DRC::testCopperTextAndGraphics( BOARD_COMMIT& aCommit )
void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, 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::vector<SHAPE*> itemShapes;
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_RECT:
{
std::vector<wxPoint> pts;
drawItem->GetRectCorners( &pts );
itemShape.emplace_back( SEG( pts[0], pts[1] ) );
itemShape.emplace_back( SEG( pts[1], pts[2] ) );
itemShape.emplace_back( SEG( pts[2], pts[3] ) );
itemShape.emplace_back( SEG( pts[3], pts[0] ) );
}
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( "DRC::testCopperDrawItem unsupported DRAWSEGMENT shape: "
+ STROKE_T_asString( drawItem->GetShape() ) );
break;
}
itemShapes = drawItem->MakeEffectiveShapes();
}
else if( textItem )
else if( textItem )
{
bbox = textItem->GetTextBox();
itemWidth = textItem->GetEffectiveTextPenWidth();
std::vector<wxPoint> textShape;
textItem->TransformTextShapeToSegmentList( textShape );
int penWidth = textItem->GetEffectiveTextPenWidth();
std::vector<wxPoint> pts;
textItem->TransformTextShapeToSegmentList( pts );
for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
itemShape.emplace_back( SEG( textShape[jj], textShape[jj+1] ) );
for( unsigned jj = 0; jj < pts.size(); jj += 2 )
itemShapes.push_back( new SHAPE_SEGMENT( pts[jj], pts[jj+1], penWidth ) );
}
else
{
@ -1008,10 +935,7 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
return;
}
SHAPE_RECT rect_area( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() );
if( itemShape.empty() )
return;
SHAPE_RECT bboxShape( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() );
// Test tracks and vias
for( auto track : m_pcb->Tracks() )
@ -1019,44 +943,42 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
if( !track->IsOnLayer( aItem->GetLayer() ) )
continue;
int minClearance = track->GetClearance( aItem, &m_clearanceSource );
int widths = ( track->GetWidth() + itemWidth ) / 2;
int center2centerAllowed = minClearance + widths;
int minClearance = track->GetClearance( aItem, &m_clearanceSource );
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;
for( const SEG& itemSeg : itemShape )
for( const SHAPE* shape : itemShapes )
{
SEG::ecoord thisDist_squared = trackSeg.SquaredDistance( itemSeg );
int this_dist;
if( !minSeg || thisDist_squared < center2center_squared )
if( shape->Collide( &trackSeg, minClearance, &this_dist ) )
{
minSeg = itemSeg;
center2center_squared = thisDist_squared;
if( this_dist < actual )
{
actual = this_dist;
pos = (wxPoint) shape->Centre();
}
}
}
if( center2center_squared < SEG::Square( center2centerAllowed ) )
if( actual < INT_MAX )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
MessageTextFromValue( userUnits(), std::max( 0, actual ), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( track, aItem );
wxPoint pos = GetLocation( track, minSeg.get() );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pos );
addMarkerToPcb( aCommit, marker );
}
@ -1073,42 +995,34 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
continue;
int minClearance = pad->GetClearance( aItem, &m_clearanceSource );
int widths = itemWidth / 2;
int center2centerAllowed = minClearance + widths;
int actual = INT_MAX;
// 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;
if( !rect_area.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) )
if( !bboxShape.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) )
continue;
SHAPE_POLY_SET padOutline;
pad->TransformShapeWithClearanceToPolygon( padOutline, 0 );
OPT<SEG> minSeg;
SEG::ecoord center2center_squared = 0;
for( const SEG& itemSeg : itemShape )
for( const std::shared_ptr<SHAPE>& aShape : pad->GetEffectiveShapes() )
{
SEG::ecoord thisCenter2center_squared = padOutline.SquaredDistance( itemSeg );
if( !minSeg || thisCenter2center_squared < center2center_squared )
for( const SHAPE* bShape : itemShapes )
{
minSeg = itemSeg;
center2center_squared = thisCenter2center_squared;
int this_dist;
if( aShape->Collide( bShape, minClearance, &this_dist ) )
actual = std::min( actual, this_dist );
}
}
if( center2center_squared < SEG::Square( center2centerAllowed ) )
if( actual < INT_MAX )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
MessageTextFromValue( userUnits(), std::max( 0, actual ), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( pad, aItem );
@ -1117,6 +1031,9 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
addMarkerToPcb( aCommit, marker );
}
}
for( SHAPE* shape : itemShapes )
delete shape;
}
@ -1217,15 +1134,6 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
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_pcb ); // 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;
@ -1260,22 +1168,12 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
continue;
}
/* Here, we must test clearance between holes and pads
* dummy pad size and shape is adjusted to pad drill size and shape
*/
if( pad->GetDrillSize().x )
{
// 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() );
int minClearance = aRefPad->GetClearance( nullptr, &m_clearanceSource );
int actual;
if( !checkClearancePadToPad( aRefPad, &dummypad, minClearance, &actual ) )
if( aRefPad->Collide( pad->GetEffectiveHoleShape(), minClearance, &actual ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
@ -1293,18 +1191,12 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
}
}
if( aRefPad->GetDrillSize().x ) // pad reference has a hole
if( aRefPad->GetDrillSize().x )
{
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() );
int minClearance = pad->GetClearance( nullptr, &m_clearanceSource );
int actual;
if( !checkClearancePadToPad( pad, &dummypad, minClearance, &actual ) )
if( pad->Collide( aRefPad->GetEffectiveHoleShape(), minClearance, &actual ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
@ -1353,7 +1245,7 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
int clearanceAllowed = minClearance - m_pcb->GetDesignSettings().GetDRCEpsilon();
int actual;
if( !checkClearancePadToPad( aRefPad, pad, clearanceAllowed, &actual ) )
if( aRefPad->Collide( pad, clearanceAllowed, &actual ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );

View File

@ -219,38 +219,6 @@ private:
void doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt,
TRACKS::iterator aEndIt, bool aTestZones );
//-----<single tests>----------------------------------------------
/**
* @param aRefPad The reference pad to check
* @param aPad Another pad to check against
* @param aMinClearance is the minimum allowed distance between the pads
* @param aActualDist [out] it the actual distance
* (only guaranteed to be set for violations)
* @return true if clearance between aRefPad and aPad is >= aMinClearance, else false
*/
bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad,
int aMinClearance, int* aActualDist );
/**
* Check the distance from a pad to segment. This function uses several
* instance variable not passed in:
* @param aPad Is the pad involved in the check
* @param aSegmentWidth width of the segment to test
* @param aMinDist Is the minimum clearance needed
* @param aActualDist [out] Is the actual clearance (only guarantted to be set on violations)
*
* @return true distance >= dist_min,
* false if distance < dist_min
*/
bool checkClearanceSegmToPad( const SEG& seg, int segWidth, const D_PAD* pad,
int minClearance, int* aActualDist );
//-----</single tests>---------------------------------------------
public:
/**
* Load the DRC rules. Must be called after the netclasses have been read.

View File

@ -30,49 +30,10 @@
#include <class_track.h>
#include <class_drawsegment.h>
#include <class_marker_pcb.h>
#include <math_for_graphics.h>
#include <geometry/polygon_test_point_inside.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_rect.h>
/*
* compare a trapezoid (can be rectangle) and a segment and return true if distance > aDist
*/
bool 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;
}
#include <geometry/shape_segment.h>
#include <convert_basic_shapes_to_polygon.h>
void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt,
@ -80,12 +41,12 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
{
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
PCB_LAYER_ID refLayer = aRefSeg->GetLayer();
LSET refLayerSet = aRefSeg->GetLayerSet();
SHAPE_SEGMENT refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd(), aRefSeg->GetWidth() );
PCB_LAYER_ID refLayer = aRefSeg->GetLayer();
LSET refLayerSet = aRefSeg->GetLayerSet();
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
int refSegWidth = aRefSeg->GetWidth();
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
int refSegWidth = aRefSeg->GetWidth();
/******************************************/
@ -319,18 +280,11 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
if( pad->GetDrillSize().x > 0 )
{
// For hole testing we use a dummy pad which is a copy of the current pad
// shrunk down to nothing but its hole.
D_PAD dummypad( *pad );
dummypad.SetSize( pad->GetDrillSize() );
dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
// Ensure the hole is on all copper layers
const static LSET all_cu = LSET::AllCuMask();
dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );
const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape();
DRC_RULE* rule = GetRule( aRefSeg, pad, CLEARANCE_CONSTRAINT );
int minClearance;
DRC_RULE* rule = GetRule( aRefSeg, &dummypad, CLEARANCE_CONSTRAINT );
int minClearance;
int actual;
if( rule )
{
@ -342,27 +296,8 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
minClearance = aRefSeg->GetClearance( nullptr, &m_clearanceSource );
}
/* Treat an oval hole as a line segment along the hole's major axis,
* shortened by half its minor axis.
* A circular hole is just a degenerate case of an oval hole.
*/
wxPoint slotStart, slotEnd;
int slotWidth;
pad->GetOblongGeometry( pad->GetDrillSize(), &slotStart, &slotEnd, &slotWidth );
slotStart += pad->GetPosition();
slotEnd += pad->GetPosition();
SEG slotSeg( slotStart, slotEnd );
int widths = ( slotWidth + refSegWidth ) / 2;
int center2centerAllowed = minClearance + widths + bds.GetDRCEpsilon();
// Avoid square-roots if possible (for performance)
SEG::ecoord center2center_squared = refSeg.SquaredDistance( slotSeg );
if( center2center_squared < SEG::Square( center2centerAllowed ) )
if( slot->Collide( &refSeg, minClearance + bds.GetDRCEpsilon(), &actual ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
@ -373,7 +308,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, pad );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, slotSeg ) );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
@ -382,13 +317,10 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
}
int minClearance = aRefSeg->GetClearance( pad, &m_clearanceSource );
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 = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
@ -399,7 +331,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, pad );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, padSeg ) );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
@ -451,17 +383,12 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
if( !trackBB.Intersects( refSegBB ) )
continue;
int minClearance = aRefSeg->GetClearance( track, &m_clearanceSource );
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 );
int minClearance = aRefSeg->GetClearance( track, &m_clearanceSource );
int actual;
SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() );
// Check two tracks crossing first as it reports a DRCE without distances
if( intersection )
if( OPT_VECTOR2I intersection = refSeg.GetSeg().Intersect( trackSeg.GetSeg() ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
drcItem->SetErrorMessage( m_msg );
@ -473,9 +400,9 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
if( !m_reportAllTrackErrors )
return;
}
else if( center2center_squared < SEG::Square( center2centerAllowed ) )
else if( refSeg.Collide( &trackSeg, minClearance, &actual ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
wxPoint pos = GetLocation( aRefSeg, trackSeg.GetSeg() );
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
@ -486,7 +413,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, track );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, trackSeg ) );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pos );
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
@ -515,28 +442,25 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
if( zone->GetNetCode() && zone->GetNetCode() == aRefSeg->GetNetCode() )
continue;
int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource );
int widths = refSegWidth / 2;
int center2centerAllowed = minClearance + widths;
SHAPE_POLY_SET* outline =
const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList( layer ) );
SEG::ecoord center2center_squared = outline->SquaredDistance( testSeg );
// 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( center2center_squared + THRESHOLD_DIST < SEG::Square( center2centerAllowed ) )
int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource );
int widths = refSegWidth / 2;
int allowedDist = minClearance + widths + THRESHOLD_DIST;
int actual;
if( zone->GetFilledPolysList( layer ).Collide( testSeg, allowedDist, &actual ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
actual = std::max( 0, actual - widths );
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, zone );
@ -616,119 +540,3 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
}
}
bool DRC::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;
}
/*
* Test if distance between a segment and a pad is > minClearance. Return the actual
* distance if it is less.
*/
bool DRC::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;
}

View File

@ -23,12 +23,11 @@
#include <drc/drc_keepout_tester.h>
#include <geometry/shape_segment.h>
#include <class_module.h>
#include <drc/drc.h>
#include <drc/drc_item.h>
DRC_KEEPOUT_TESTER::DRC_KEEPOUT_TESTER( MARKER_HANDLER aMarkerHandler ) :
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ),
m_units( EDA_UNITS::MILLIMETRES ),
@ -91,11 +90,9 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias()
if( !m_zone->IsOnLayer( segm->GetLayer() ) )
continue;
int widths = segm->GetWidth() / 2;
SEG trackSeg( segm->GetStart(), segm->GetEnd() );
SEG::ecoord center2center_squared = m_zone->Outline()->SquaredDistance( trackSeg );
SEG trackSeg( segm->GetStart(), segm->GetEnd() );
if( center2center_squared <= SEG::Square( widths) )
if( m_zone->Outline()->Collide( trackSeg, segm->GetWidth() / 2 ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_KEEPOUT );
@ -111,42 +108,37 @@ bool DRC_KEEPOUT_TESTER::checkTracksAndVias()
}
else if( segm->Type() == PCB_VIA_T && ( m_keepoutFlags & CHECK_VIAS_MASK ) != 0 )
{
VIA* via = static_cast<VIA*>( segm );
int sourceId = 0;
VIA* via = static_cast<VIA*>( segm );
SEG seg( via->GetPosition(), via->GetPosition() );
int test = 0;
int clearance = via->GetWidth() / 2;
if( ( m_keepoutFlags & DISALLOW_VIAS ) > 0 )
{
sourceId = DISALLOW_VIAS;
test = DISALLOW_VIAS;
}
else if( via->GetViaType() == VIATYPE::MICROVIA
&& ( m_keepoutFlags & DISALLOW_MICRO_VIAS ) > 0 )
{
sourceId = DISALLOW_MICRO_VIAS;
test = DISALLOW_MICRO_VIAS;
}
else if( via->GetViaType() == VIATYPE::BLIND_BURIED
&& ( m_keepoutFlags & DISALLOW_BB_VIAS ) > 0 )
{
sourceId = DISALLOW_BB_VIAS;
test = DISALLOW_BB_VIAS;
}
else if( ( m_keepoutFlags & DISALLOW_HOLES ) > 0 )
{
sourceId = DISALLOW_HOLES;
test = DISALLOW_HOLES;
clearance = via->GetDrillValue() / 2;
}
else
continue;
int widths = via->GetWidth() / 2;
wxPoint viaPos = via->GetPosition();
if( sourceId == DISALLOW_HOLES )
widths = via->GetDrillValue() / 2;
SEG::ecoord center2center_squared = m_zone->Outline()->SquaredDistance( viaPos );
if( center2center_squared <= SEG::Square( widths ) )
if( m_zone->Outline()->Collide( seg, clearance ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_KEEPOUT );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ), m_sources.at( sourceId ) );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s)" ), m_sources.at( test ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( segm, m_zone );
@ -259,18 +251,9 @@ bool DRC_KEEPOUT_TESTER::checkPads( MODULE* aModule )
}
else if( ( m_keepoutFlags & DISALLOW_HOLES ) > 0 )
{
wxPoint slotStart, slotEnd;
int slotWidth;
const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape();
pad->GetOblongGeometry( pad->GetDrillSize(), &slotStart, &slotEnd, &slotWidth );
slotStart += pad->GetPosition();
slotEnd += pad->GetPosition();
SEG slotSeg( slotStart, slotEnd );
SHAPE_POLY_SET* outline = m_zone->Outline();
SEG::ecoord center2center_sq = outline->SquaredDistance( slotSeg );
if( center2center_sq <= SEG::Square( slotWidth) )
if( m_zone->Outline()->Collide( slot->GetSeg(), slot->GetWidth() / 2 ) )
{
DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_KEEPOUT );

View File

@ -765,8 +765,7 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
// Choose drawing settings depending on if we are drawing a pad itself or a hole
if( aLayer == LAYER_PADS_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
{
const std::shared_ptr<SHAPE_SEGMENT>& seg = aPad->GetEffectiveHoleShape();
const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() );
}
else

View File

@ -698,7 +698,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
else
{
// Note: small drill marks have no significance when applied to slots
const std::shared_ptr<SHAPE_SEGMENT>& seg = pad->GetEffectiveHoleShape();
const SHAPE_SEGMENT* seg = pad->GetEffectiveHoleShape();
aPlotter->ThickSegment( (wxPoint) seg->GetSeg().A,
(wxPoint) seg->GetSeg().B,
seg->GetWidth(), SKETCH, NULL );