From a56191b658d443608d0c5d525feed389fa02260b Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sun, 26 Apr 2020 11:30:32 +0100 Subject: [PATCH] Add distances to a bunch of DRC violation reports. --- common/draw_panel_gal.cpp | 12 + eeschema/sch_sheet.cpp | 2 +- libs/kimath/include/geometry/shape_rect.h | 2 + libs/kimath/src/geometry/shape_collisions.cpp | 36 +- pcbnew/drc/drc.cpp | 135 +- pcbnew/drc/drc.h | 67 +- pcbnew/drc/drc_clearance_test_functions.cpp | 1352 ++++------------- 7 files changed, 418 insertions(+), 1188 deletions(-) diff --git a/common/draw_panel_gal.cpp b/common/draw_panel_gal.cpp index 90c512de54..a3c7f602a5 100644 --- a/common/draw_panel_gal.cpp +++ b/common/draw_panel_gal.cpp @@ -67,6 +67,18 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin SetLayoutDirection( wxLayout_LeftToRight ); + // If we're in a dialog, we have to go looking for our parent frame + if( !m_edaFrame ) + { + wxWindow* ancestor = aParentWindow->GetParent(); + + while( ancestor && !dynamic_cast( ancestor ) ) + ancestor = ancestor->GetParent(); + + if( ancestor ) + m_edaFrame = dynamic_cast( ancestor ); + } + SwitchBackend( aGalType ); SetBackgroundStyle( wxBG_STYLE_CUSTOM ); diff --git a/eeschema/sch_sheet.cpp b/eeschema/sch_sheet.cpp index a43bfe036d..c149f947e2 100644 --- a/eeschema/sch_sheet.cpp +++ b/eeschema/sch_sheet.cpp @@ -941,7 +941,7 @@ void SCH_SHEET::GetNetListItem( NETLIST_OBJECT_LIST& aNetListItems, SCH_SHEET_PA item->m_Start = item->m_End = sheetPin->GetPosition(); aNetListItems.push_back( item ); - if( SCH_CONNECTION::IsBusLabel( sheetPin->GetText() ) ) + if( SCH_CONNECTION::IsBusLabel( sheetPin->GetShownText() ) ) item->ConvertBusToNetListItems( aNetListItems ); } } diff --git a/libs/kimath/include/geometry/shape_rect.h b/libs/kimath/include/geometry/shape_rect.h index 6915f2df72..f1a2fdcda2 100644 --- a/libs/kimath/include/geometry/shape_rect.h +++ b/libs/kimath/include/geometry/shape_rect.h @@ -93,6 +93,8 @@ public: /// @copydoc SHAPE::Collide() bool Collide( const SEG& aSeg, int aClearance = 0 ) const override; + bool DoCollide( const SEG& aSeg, int aClearance, int* aActualDist ) const; + /** * Function GetPosition() * diff --git a/libs/kimath/src/geometry/shape_collisions.cpp b/libs/kimath/src/geometry/shape_collisions.cpp index 0e7f66f325..d22c036d51 100644 --- a/libs/kimath/src/geometry/shape_collisions.cpp +++ b/libs/kimath/src/geometry/shape_collisions.cpp @@ -562,28 +562,38 @@ bool SHAPE::Collide( const SHAPE* aShape, int aClearance ) const bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance ) const { - //VECTOR2I pmin = VECTOR2I( std::min( aSeg.a.x, aSeg.b.x ), std::min( aSeg.a.y, aSeg.b.y ) ); - //VECTOR2I pmax = VECTOR2I( std::max( aSeg.a.x, aSeg.b.x ), std::max( aSeg.a.y, aSeg.b.y )); - //BOX2I r( pmin, VECTOR2I( pmax.x - pmin.x, pmax.y - pmin.y ) ); + int dummy; + return DoCollide( aSeg, aClearance, &dummy ); +} - //if( BBox( 0 ).SquaredDistance( r ) > aClearance * aClearance ) - // return false; +bool SHAPE_RECT::DoCollide( const SEG& aSeg, int aClearance, int* aActualDist ) const +{ if( BBox( 0 ).Contains( aSeg.A ) || BBox( 0 ).Contains( aSeg.B ) ) + { + *aActualDist = 0; return true; + } VECTOR2I vts[] = { VECTOR2I( m_p0.x, m_p0.y ), - VECTOR2I( m_p0.x, m_p0.y + m_h ), - VECTOR2I( m_p0.x + m_w, m_p0.y + m_h ), - VECTOR2I( m_p0.x + m_w, m_p0.y ), - VECTOR2I( m_p0.x, m_p0.y ) }; + VECTOR2I( m_p0.x, m_p0.y + m_h ), + VECTOR2I( m_p0.x + m_w, m_p0.y + m_h ), + VECTOR2I( m_p0.x + m_w, m_p0.y ), + VECTOR2I( m_p0.x, m_p0.y ) }; - for( int i = 0; i < 4; i++ ) + SEG s( vts[0], vts[1] ); + SEG::ecoord dist_squared = s.SquaredDistance( aSeg ); + + for( int i = 1; i < 4; i++ ) { - SEG s( vts[i], vts[i + 1], i ); + s = SEG( vts[i], vts[i + 1] ); + dist_squared = std::min( dist_squared, s.SquaredDistance( aSeg ) ); + } - if( s.Distance( aSeg ) < aClearance ) - return true; + if( dist_squared < SEG::ecoord( aClearance ) * SEG::ecoord( aClearance ) ) + { + *aActualDist = sqrt( dist_squared ); + return true; } return false; diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp index cd7bd5127f..132ad92da5 100644 --- a/pcbnew/drc/drc.cpp +++ b/pcbnew/drc/drc.cpp @@ -72,14 +72,6 @@ DRC::DRC() : m_drcRun = false; m_footprintsTested = false; - - m_segmAngle = 0; - m_segmLength = 0; - - m_xcliplo = 0; - m_ycliplo = 0; - m_xcliphi = 0; - m_ycliphi = 0; } @@ -178,8 +170,9 @@ void DRC::DestroyDRCDialog( int aReason ) int DRC::TestZoneToZoneOutlines() { - BOARD* board = m_pcbEditorFrame->GetBoard(); - int nerrors = 0; + BOARD* board = m_pcbEditorFrame->GetBoard(); + int nerrors = 0; + wxString msg; std::vector smoothed_polys; smoothed_polys.resize( board->GetAreaCount() ); @@ -273,7 +266,7 @@ int DRC::TestZoneToZoneOutlines() } // Iterate through all the segments of refSmoothedPoly - std::set conflictPoints; + std::map conflictPoints; for( auto refIt = smoothed_polys[ia].IterateSegmentsWithHoles(); refIt; refIt++ ) { @@ -307,16 +300,27 @@ int DRC::TestZoneToZoneOutlines() &pt.x, &pt.y ); if( d < zone2zoneClearance ) - conflictPoints.insert( pt ); + { + if( conflictPoints.count( pt ) ) + conflictPoints[ pt ] = std::min( conflictPoints[ pt ], d ); + else + conflictPoints[ pt ] = d; + } } } - for( wxPoint pt : conflictPoints ) + for( const std::pair& conflict : conflictPoints ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_ZONES_TOO_CLOSE ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), zone2zoneClearance ), + MessageTextFromValue( userUnits(), conflict.second ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( zoneRef, zoneToTest ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, pt ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, conflict.first ); addMarkerToPcb( marker ); nerrors++; } @@ -534,17 +538,16 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg ) const BOARD_DESIGN_SETTINGS& g = m_pcb->GetDesignSettings(); -#define FmtVal( x ) GetChars( StringFromValue( m_pcbEditorFrame->GetUserUnits(), x ) ) - #if 0 // set to 1 when (if...) BOARD_DESIGN_SETTINGS has a m_MinClearance value if( nc->GetClearance() < g.m_MinClearance ) { - msg.Printf( _( "NETCLASS: \"%s\" has Clearance:%s which is less than global:%s" ), - nc->GetName(), - FmtVal( nc->GetClearance() ), - FmtVal( g.m_TrackClearance ) ); - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_CLEARANCE ); + + msg.Printf( drcItem->GetErrorText() + _( "(global minimum %s; '%s' minimum %s)" ), + MessageTextFromValue( userUnits(), g.m_TrackClearance, true ), + nc->GetName(), + MessageTextFromValue( userUnits(), nc->GetClearance(), true ) ); + drcItem->SetErrorMessage( msg ); addMarkerToPcb( new MARKER_PCB( drcItem, wxPoint() ) ); ret = false; @@ -553,12 +556,13 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg ) if( nc->GetTrackWidth() < g.m_TrackMinWidth ) { - msg.Printf( _( "NETCLASS: \"%s\" has TrackWidth:%s which is less than global:%s" ), - nc->GetName(), - FmtVal( nc->GetTrackWidth() ), - FmtVal( g.m_TrackMinWidth ) ); - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_TRACKWIDTH ); + + msg.Printf( drcItem->GetErrorText() + _( "(global minimum %s; '%s' minimum %s)" ), + MessageTextFromValue( userUnits(), g.m_TrackMinWidth, true ), + nc->GetName(), + MessageTextFromValue( userUnits(), nc->GetTrackWidth(), true ) ); + drcItem->SetErrorMessage( msg ); addMarkerToPcb( new MARKER_PCB( drcItem, wxPoint() ) ); ret = false; @@ -566,12 +570,13 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg ) if( nc->GetViaDiameter() < g.m_ViasMinSize ) { - msg.Printf( _( "NETCLASS: \"%s\" has Via Dia:%s which is less than global:%s" ), - nc->GetName(), - FmtVal( nc->GetViaDiameter() ), - FmtVal( g.m_ViasMinSize ) ); - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_VIASIZE ); + + msg.Printf( drcItem->GetErrorText() + _( "(global minimum %s; '%s' minimum %s)" ), + MessageTextFromValue( userUnits(), g.m_ViasMinSize, true ), + nc->GetName(), + MessageTextFromValue( userUnits(), nc->GetViaDiameter(), true ) ); + drcItem->SetErrorMessage( msg ); addMarkerToPcb( new MARKER_PCB( drcItem, wxPoint() ) ); ret = false; @@ -579,12 +584,13 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg ) if( nc->GetViaDrill() < g.m_ViasMinDrill ) { - msg.Printf( _( "NETCLASS: \"%s\" has Via Drill:%s which is less than global:%s" ), - nc->GetName(), - FmtVal( nc->GetViaDrill() ), - FmtVal( g.m_ViasMinDrill ) ); - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_VIADRILLSIZE ); + + msg.Printf( drcItem->GetErrorText() + _( "(global minimum %s; '%s' minimum %s)" ), + MessageTextFromValue( userUnits(), g.m_ViasMinDrill, true ), + nc->GetName(), + MessageTextFromValue( userUnits(), nc->GetViaDrill(), true ) ); + drcItem->SetErrorMessage( msg ); addMarkerToPcb( new MARKER_PCB( drcItem, wxPoint() ) ); ret = false; @@ -592,12 +598,13 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg ) if( nc->GetuViaDiameter() < g.m_MicroViasMinSize ) { - msg.Printf( _( "NETCLASS: \"%s\" has uVia Dia:%s which is less than global:%s" ), - nc->GetName(), - FmtVal( nc->GetuViaDiameter() ), - FmtVal( g.m_MicroViasMinSize ) ); - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_uVIASIZE ); + + msg.Printf( drcItem->GetErrorText() + _( "(global minimum %s; '%s' minimum %s)" ), + MessageTextFromValue( userUnits(), g.m_MicroViasMinSize, true ), + nc->GetName(), + MessageTextFromValue( userUnits(), nc->GetuViaDiameter(), true ) ); + drcItem->SetErrorMessage( msg ); addMarkerToPcb( new MARKER_PCB( drcItem, wxPoint() ) ); ret = false; @@ -605,12 +612,13 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg ) if( nc->GetuViaDrill() < g.m_MicroViasMinDrill ) { - msg.Printf( _( "NETCLASS: \"%s\" has uVia Drill:%s which is less than global:%s" ), - nc->GetName(), - FmtVal( nc->GetuViaDrill() ), - FmtVal( g.m_MicroViasMinDrill ) ); - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_NETCLASS_uVIADRILLSIZE ); + + msg.Printf( drcItem->GetErrorText() + _( "(global minimum %s; '%s' minimum %s)" ), + MessageTextFromValue( userUnits(), g.m_MicroViasMinDrill, true ), + nc->GetName(), + MessageTextFromValue( userUnits(), nc->GetuViaDrill(), true ) ); + drcItem->SetErrorMessage( msg ); addMarkerToPcb( new MARKER_PCB( drcItem, wxPoint() ) ); ret = false; @@ -1046,7 +1054,7 @@ void DRC::testCopperDrawItem( DRAWSEGMENT* aItem ) DRC_ITEM* drcItem = new DRC_ITEM( DRCE_VIA_NEAR_COPPER ); drcItem->SetItems( track, aItem ); - wxPoint pos = getLocation( track, aItem, itemSeg ); + wxPoint pos = getLocation( track, itemSeg ); MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); addMarkerToPcb( marker ); } @@ -1055,7 +1063,7 @@ void DRC::testCopperDrawItem( DRAWSEGMENT* aItem ) DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_COPPER ); drcItem->SetItems( track, aItem ); - wxPoint pos = getLocation( track, aItem, itemSeg ); + wxPoint pos = getLocation( track, itemSeg ); MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); addMarkerToPcb( marker ); } @@ -1136,7 +1144,7 @@ void DRC::testCopperTextItem( BOARD_ITEM* aTextItem ) DRC_ITEM* drcItem = new DRC_ITEM( DRCE_VIA_NEAR_COPPER ); drcItem->SetItems( track, aTextItem ); - wxPoint pos = getLocation( track, aTextItem, textSeg ); + wxPoint pos = getLocation( track, textSeg ); MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); addMarkerToPcb( marker ); } @@ -1145,7 +1153,7 @@ void DRC::testCopperTextItem( BOARD_ITEM* aTextItem ) DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_COPPER ); drcItem->SetItems( track, aTextItem ); - wxPoint pos = getLocation( track, aTextItem, textSeg ); + wxPoint pos = getLocation( track, textSeg ); MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); addMarkerToPcb( marker ); } @@ -1329,8 +1337,6 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li for( D_PAD** pad_list = aStart; pad_listGetOrientation() ); - if( !checkClearancePadToPad( aRefPad, &dummypad, &allowed, &actual ) ) + int minClearance = aRefPad->GetClearance( &dummypad ); + int actual; + + if( !checkClearancePadToPad( aRefPad, &dummypad, minClearance, &actual ) ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_NEAR_PAD ); msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), allowed, true ), + MessageTextFromValue( userUnits(), minClearance, true ), MessageTextFromValue( userUnits(), actual, true ) ); drcItem->SetErrorMessage( msg ); @@ -1400,12 +1409,15 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); dummypad.SetOrientation( aRefPad->GetOrientation() ); - if( !checkClearancePadToPad( pad, &dummypad, &allowed, &actual ) ) + int minClearance = pad->GetClearance( &dummypad ); + int actual; + + if( !checkClearancePadToPad( pad, &dummypad, minClearance, &actual ) ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_HOLE_NEAR_PAD ); msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), allowed, true ), + MessageTextFromValue( userUnits(), minClearance, true ), MessageTextFromValue( userUnits(), actual, true ) ); drcItem->SetErrorMessage( msg ); @@ -1444,12 +1456,15 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li continue; } - if( !checkClearancePadToPad( aRefPad, pad, &allowed, &actual ) ) + int minClearance = aRefPad->GetClearance( &dummypad ); + int actual; + + if( !checkClearancePadToPad( aRefPad, pad, minClearance, &actual ) ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_PAD_NEAR_PAD1 ); msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), - MessageTextFromValue( userUnits(), allowed, true ), + MessageTextFromValue( userUnits(), minClearance, true ), MessageTextFromValue( userUnits(), actual, true ) ); drcItem->SetErrorMessage( msg ); @@ -1572,7 +1587,7 @@ wxPoint DRC::getLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone ) const } -wxPoint DRC::getLocation( TRACK* aTrack, BOARD_ITEM* aConflitItem, const SEG& aConflictSeg ) const +wxPoint DRC::getLocation( TRACK* aTrack, const SEG& aConflictSeg ) const { wxPoint pt1 = aTrack->GetPosition(); wxPoint pt2 = aTrack->GetEnd(); @@ -1580,7 +1595,7 @@ wxPoint DRC::getLocation( TRACK* aTrack, BOARD_ITEM* aConflitItem, const SEG& aC // Do a binary search along the track for a "good enough" marker location while( GetLineLength( pt1, pt2 ) > EPSILON ) { - if( aConflictSeg.Distance( pt1 ) < aConflictSeg.Distance( pt2 ) ) + if( aConflictSeg.SquaredDistance( pt1 ) < aConflictSeg.SquaredDistance( pt2 ) ) pt2 = ( pt1 + pt2 ) / 2; else pt1 = ( pt1 + pt2 ) / 2; diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index 02edbcfae8..b46e9f8280 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -34,10 +34,6 @@ #include #include -#define OK_DRC 0 -#define BAD_DRC 1 - - /// DRC error codes: enum PCB_DRC_CODE { @@ -155,29 +151,6 @@ private: bool m_reportAllTrackErrors; // Report all tracks errors (or only 4 first errors) bool m_testFootprints; // Test footprints against schematic - /* In DRC functions, many calculations are using coordinates relative - * to the position of the segment under test (segm to segm DRC, segm to pad DRC - * Next variables store coordinates relative to the start point of this segment - */ - wxPoint m_padToTestPos; // Position of the pad for segm-to-pad and pad-to-pad - wxPoint m_segmEnd; // End point of the reference segment (start = (0, 0) ) - - /* Some functions are comparing the ref segm to pads or others segments using - * coordinates relative to the ref segment considered as the X axis - * so we store the ref segment length (the end point relative to these axis) - * and the segment orientation (used to rotate other coordinates) - */ - double m_segmAngle; // Ref segm orientation in 0.1 degree - int m_segmLength; // length of the reference segment - - /* variables used in checkLine to test DRC segm to segm: - * define the area relative to the ref segment that does not contains any other segment - */ - int m_xcliplo; - int m_ycliplo; - int m_xcliphi; - int m_ycliphi; - PCB_EDIT_FRAME* m_pcbEditorFrame; // The pcb frame editor which owns the board BOARD* m_pcb; SHAPE_POLY_SET m_board_outlines; // The board outline including cutouts @@ -207,7 +180,7 @@ private: * Fetches a reasonable point for marking a violoation between two non-point objects. */ wxPoint getLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone ) const; - wxPoint getLocation( TRACK* aTrack, BOARD_ITEM* aConflitItem, const SEG& aConflictSeg ) const; + wxPoint getLocation( TRACK* aTrack, const SEG& aConflictSeg ) const; //---------------------------------------------- @@ -301,16 +274,17 @@ private: /** * @param aRefPad The reference pad to check * @param aPad Another pad to check against - * @param aAllowed [out] is the allowed distance (only guaranteed to be set for violations) - * @param aActual [out] it the actual difference (only guaranteed to be set for violations) - * @return bool - true if clearance between aRefPad and aPad is >= dist_min, else false + * @param aMinClearance is the minimum allowed distance between the pads + * @param aActual [out] it the actual distance (only guaranteed to be set for violations) + * @return bool - true if clearance between aRefPad and aPad is >= aMinClearance, else false */ - bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, int* aActual ); + bool checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual ); /** * Check the distance from a pad to segment. This function uses several * instance variable not passed in: + * // JEY TODO: nuke these: * m_segmLength = length of the segment being tested * m_segmAngle = angle of the segment with the X axis; * m_segmEnd = end coordinate of the segment @@ -323,35 +297,10 @@ private: * @return true distance >= dist_min, * false if distance < dist_min */ - bool checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist, - int* aActualDist ); + bool checkClearanceSegmToPad( const SEG& seg, int segWidth, const D_PAD* pad, + int minClearance, int* aActualDist ); - /** - * Check the distance from a point to a segment. - * - * The segment is expected starting at 0,0, and on the X axis - * (used to test DRC between a segment and a round pad, via or round end of a track - * @param aCentre The coordinate of the circle's center - * @param aAllowed A "keep out" radius centered over the circle - * @param aLength The length of the segment (i.e. coordinate of end, because it is on - * the X axis) - * @param aActual [out] is the actual distance (only guaranteed to be set on violations) - * @return bool - true if distance >= radius, else - * false when distance < aAllowed - */ - static bool checkMarginToCircle( wxPoint aCentre, int aAllowed, int aLength, int* aActual ); - - - /** - * Function checkLine - * (helper function used in drc calculations to see if one track is in contact with - * another track). - * Test if a line intersects a bounding box (a rectangle) - * The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi - * return true if the line from aSegStart to aSegEnd is outside the bounding box - */ - bool checkLine( wxPoint aSegStart, wxPoint aSegEnd ); //-------------------------------------------------- diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index 5541913011..5976d72713 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -39,9 +39,16 @@ #include #include #include // for KiROUND +#include #include +static SEG::ecoord square( int a ) +{ + return SEG::ecoord( a ) * a; +} + + /** * 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 @@ -138,25 +145,15 @@ bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt, bool aTestZones ) { - TRACK* track; - wxPoint delta; // length on X and Y axis of segments - wxPoint shape_pos; - - NETCLASSPTR netclass = aRefSeg->GetNetClass(); BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings(); + wxString msg; - // In order to make some calculations more easier or faster, pads and tracks - // coordinates will be made relative to the reference segment origin - wxPoint origin = aRefSeg->GetStart(); - - m_segmEnd = delta = aRefSeg->GetEnd() - origin; - m_segmAngle = 0; - - LSET layerMask = aRefSeg->GetLayerSet(); - int net_code_ref = aRefSeg->GetNetCode(); - int ref_seg_clearance = netclass->GetClearance(); - int ref_seg_width = aRefSeg->GetWidth(); - int actual; + SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); + NETCLASSPTR netclass = aRefSeg->GetNetClass(); + LSET layerMask = aRefSeg->GetLayerSet(); + int refNetCode = aRefSeg->GetNetCode(); + int refSegWidth = aRefSeg->GetWidth(); + int refSegClearance = netclass->GetClearance(); /******************************************/ @@ -173,6 +170,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), dsnSettings.m_MicroViasMinSize, true ), + MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( refvia ); MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() ); @@ -182,6 +185,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA_DRILL ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), dsnSettings.m_MicroViasMinDrill, true ), + MessageTextFromValue( userUnits(), refvia->GetDrillValue(), true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( refvia ); MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() ); @@ -193,6 +202,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( refvia->GetWidth() < dsnSettings.m_ViasMinSize ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), dsnSettings.m_ViasMinSize, true ), + MessageTextFromValue( userUnits(), refvia->GetWidth(), true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( refvia ); MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() ); @@ -202,6 +217,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_DRILL ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), dsnSettings.m_ViasMinDrill, true ), + MessageTextFromValue( userUnits(), refvia->GetDrillValue(), true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( refvia ); MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() ); @@ -215,6 +236,12 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( refvia->GetDrillValue() > refvia->GetWidth() ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_VIA_HOLE_BIGGER ); + + msg.Printf( drcItem->GetErrorText() + _( "(diameter %s; drill %s)" ), + MessageTextFromValue( userUnits(), refvia->GetWidth(), true ), + MessageTextFromValue( userUnits(), refvia->GetDrillValue(), true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( refvia ); MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() ); @@ -272,11 +299,17 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } else // This is a track segment { - if( ref_seg_width < dsnSettings.m_TrackMinWidth ) + if( refSegWidth < dsnSettings.m_TrackMinWidth ) { wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2; DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_TRACK_WIDTH ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), dsnSettings.m_TrackMinWidth, true ), + MessageTextFromValue( userUnits(), refSegWidth, true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( aRefSeg ); MARKER_PCB* marker = new MARKER_PCB( drcItem, refsegMiddle ); @@ -284,94 +317,89 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } } - // for a non horizontal or vertical segment Compute the segment angle - // in tenths of degrees and its length - if( delta.x || delta.y ) - { - // Compute the segment angle in 0,1 degrees - m_segmAngle = ArcTangente( delta.y, delta.x ); - - // Compute the segment length: we build an equivalent rotated segment, - // this segment is horizontal, therefore dx = length - RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0 - } - - m_segmLength = delta.x; /******************************************/ /* Phase 1 : test DRC track to pads : */ /******************************************/ - /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers - * but having a hole - * This dummy pad has the size and shape of the hole - * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function. - * Therefore, this dummy pad is a circle or an oval. - * A pad must have a parent because some functions expect a non null parent - * to find the parent board, and some other data - */ - MODULE dummymodule( m_pcb ); // Creates a dummy parent - D_PAD dummypad( &dummymodule ); - - dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers - // Compute the min distance to pads for( MODULE* mod : m_pcb->Modules() ) { for( D_PAD* pad : mod->Pads() ) { - SEG padSeg( pad->GetPosition(), pad->GetPosition() ); - - // No problem if pads are on another layer, but if a drill hole exists (a pad on - // a single layer can have a hole!) we must test the hole - if( !( pad->GetLayerSet() & layerMask ).any() ) + if( pad->GetDrillSize().x > 0 ) { - // We must test the pad hole. In order to use checkClearanceSegmToPad(), a - // pseudo pad is used, with a shape and a size like the hole - if( pad->GetDrillSize().x == 0 ) - continue; + /* 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 = pad->GetPosition(); + wxPoint slotEnd = pad->GetPosition(); + int slotHalfWidth; - dummypad.SetSize( pad->GetDrillSize() ); - dummypad.SetPosition( pad->GetPosition() ); - dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? - PAD_SHAPE_OVAL : - PAD_SHAPE_CIRCLE ); - dummypad.SetOrientation( pad->GetOrientation() ); - - m_padToTestPos = dummypad.GetPosition() - origin; - - if( !checkClearanceSegmToPad( &dummypad, ref_seg_width, ref_seg_clearance, &actual ) ) + if( pad->GetDrillSize().x > pad->GetDrillSize().y ) { + slotHalfWidth = pad->GetDrillSize().y / 2; + slotStart.x -= ( pad->GetDrillSize().x / 2 ) - slotHalfWidth; + slotEnd.x += ( pad->GetDrillSize().x / 2 ) - slotHalfWidth; + } + else + { + slotHalfWidth = pad->GetDrillSize().x / 2; + slotStart.y -= ( pad->GetDrillSize().y / 2 ) - slotHalfWidth; + slotEnd.y += ( pad->GetDrillSize().y / 2 ) - slotHalfWidth; + } + + SEG slotSeg( slotStart, slotEnd ); + int widths = slotHalfWidth + ( refSegWidth / 2 ); + int center2centerAllowed = refSegClearance + widths; + + // Avoid square-roots if possible (for performance) + SEG::ecoord center2center_squared = refSeg.SquaredDistance( slotSeg ); + + if( center2center_squared < square( center2centerAllowed ) ) + { + int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_THROUGH_HOLE ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), refSegClearance, true ), + MessageTextFromValue( userUnits(), actual, true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( aRefSeg, pad ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, pad, padSeg ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, slotSeg ) ); addMarkerToPcb( marker ); if( !m_reportAllTrackErrors ) return; } - - continue; } - // The pad must be in a net (i.e pt_pad->GetNet() != 0 ) - // but no problem if the pad netcode is the current netcode (same net) - if( pad->GetNetCode() // the pad must be connected - && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok + if( !( pad->GetLayerSet() & layerMask ).any() ) continue; - // DRC for the pad - shape_pos = pad->ShapePos(); - m_padToTestPos = shape_pos - origin; - int segToPadClearance = std::max( ref_seg_clearance, pad->GetClearance() ); + // No need to check pads with the same net as the refSeg. + if( pad->GetNetCode() && refNetCode == pad->GetNetCode() ) + continue; - if( !checkClearanceSegmToPad( pad, ref_seg_width, segToPadClearance, &actual ) ) + SEG padSeg( pad->GetPosition(), pad->GetPosition() ); + int minClearance = std::max( refSegClearance, pad->GetClearance() ); + int actual; + + if( !checkClearanceSegmToPad( refSeg, refSegWidth, pad, minClearance, &actual ) ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_PAD ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), minClearance, true ), + MessageTextFromValue( userUnits(), actual, true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( aRefSeg, pad ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, pad, padSeg ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, padSeg ) ); addMarkerToPcb( marker ); if( !m_reportAllTrackErrors ) @@ -384,340 +412,67 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato /* Phase 2: test DRC with other track segments */ /***********************************************/ - // At this point the reference segment is the X axis - // Test the reference segment with other track segments - wxPoint segStartPoint; - wxPoint segEndPoint; - for( auto it = aStartIt; it != aEndIt; it++ ) { - track = *it; + TRACK* track = *it; + // No problem if segments have the same net code: - if( net_code_ref == track->GetNetCode() ) + if( refNetCode == track->GetNetCode() ) continue; - // No problem if segment are on different layers : + // No problem if tracks are on different layers : if( !( layerMask & track->GetLayerSet() ).any() ) continue; - // the minimum distance = clearance plus half the reference track - // width plus half the other track's width - int w_dist = std::max( ref_seg_clearance, track->GetClearance() ); - w_dist += ( ref_seg_width + track->GetWidth() ) / 2; + SEG trackSeg( track->GetStart(), track->GetEnd() ); + int minClearance = std::max( refSegClearance, track->GetClearance() ); + int widths = ( refSegWidth + track->GetWidth() ) / 2; + int center2centerAllowed = minClearance + widths; - // Due to many double to int conversions during calculations, which - // create rounding issues, - // the exact clearance margin cannot be really known. - // To avoid false bad DRC detection due to these rounding issues, - // slightly decrease the w_dist (remove one nanometer is enough !) - w_dist -= 1; + // Avoid square-roots if possible (for performance) + SEG::ecoord center2center_squared = refSeg.SquaredDistance( trackSeg ); + OPT_VECTOR2I intersection = refSeg.Intersect( trackSeg ); - // If the reference segment is a via, we test it here - if( aRefSeg->Type() == PCB_VIA_T ) + // Check two tracks crossing first as it reports a DRCE without distances + if( intersection ) { - delta = track->GetEnd() - track->GetStart(); - segStartPoint = aRefSeg->GetStart() - track->GetStart(); - - if( track->Type() == PCB_VIA_T ) - { - // Test distance between two vias, i.e. two circles, trivial case - if( EuclideanNorm( segStartPoint ) < w_dist ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_VIA_NEAR_VIA ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefSeg->GetPosition() ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - else // test via to segment - { - // Compute l'angle du segment a tester; - double angle = ArcTangente( delta.y, delta.x ); - - // Compute new coordinates ( the segment become horizontal) - RotatePoint( &delta, angle ); - RotatePoint( &segStartPoint, angle ); - - if( !checkMarginToCircle( segStartPoint, w_dist, delta.x, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_VIA_NEAR_TRACK ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefSeg->GetPosition() ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - - continue; - } - - /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for - * the segment to test in the new axis : the new X axis is the - * reference segment. We must translate and rotate the segment to test - */ - segStartPoint = track->GetStart() - origin; - segEndPoint = track->GetEnd() - origin; - RotatePoint( &segStartPoint, m_segmAngle ); - RotatePoint( &segEndPoint, m_segmAngle ); - - SEG seg( segStartPoint, segEndPoint ); - - if( track->Type() == PCB_VIA_T ) - { - if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength, &actual ) ) - continue; - - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_VIA ); + DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACKS_CROSSING ); + drcItem->SetErrorMessage( msg ); drcItem->SetItems( aRefSeg, track ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); + MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) intersection.get() ); addMarkerToPcb( marker ); if( !m_reportAllTrackErrors ) return; } - - /* We have changed axis: - * the reference segment is Horizontal. - * 3 cases : the segment to test can be parallel, perpendicular or have another direction - */ - if( segStartPoint.y == segEndPoint.y ) // parallel segments + else if( center2center_squared < square( center2centerAllowed ) ) { - if( abs( segStartPoint.y ) >= w_dist ) - continue; + int errorCode = DRCE_TRACK_ENDS; - // Ensure segStartPoint.x <= segEndPoint.x - if( segStartPoint.x > segEndPoint.x ) - std::swap( segStartPoint.x, segEndPoint.x ); + 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; - if( segStartPoint.x > ( -w_dist ) && segStartPoint.x < ( m_segmLength + w_dist ) ) - { - // the start point is inside the reference range - // X........ - // O--REF--+ + int actual = std::max( 0.0, sqrt( center2center_squared ) - widths ); + DRC_ITEM* drcItem = new DRC_ITEM( errorCode ); - // Fine test : we consider the rounded shape of each end of the track segment: - if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), minClearance, true ), + MessageTextFromValue( userUnits(), actual, true ) ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( aRefSeg, track ); - if( !m_reportAllTrackErrors ) - return; - } + MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, trackSeg ) ); + addMarkerToPcb( marker ); - if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - - if( segEndPoint.x > ( -w_dist ) && segEndPoint.x < ( m_segmLength + w_dist ) ) - { - // the end point is inside the reference range - // .....X - // O--REF--+ - // Fine test : we consider the rounded shape of the ends - if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - - if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - - if( segStartPoint.x <= 0 && segEndPoint.x >= 0 ) - { - // the segment straddles the reference range (this actually only - // checks if it straddles the origin, because the other cases where already - // handled) - // X.............X - // O--REF--+ - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_SEGMENTS_TOO_CLOSE ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments - { - if( segStartPoint.x <= -w_dist || segStartPoint.x >= m_segmLength + w_dist ) - continue; - - // Test if segments are crossing - if( segStartPoint.y > segEndPoint.y ) - std::swap( segStartPoint.y, segEndPoint.y ); - - if( ( segStartPoint.y < 0 ) && ( segEndPoint.y > 0 ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACKS_CROSSING ); - drcItem->SetItems( aRefSeg, track ); - - wxPoint pos( track->GetStart().x, aRefSeg->GetStart().y ); - MARKER_PCB* marker = new MARKER_PCB( drcItem, pos ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - - // At this point the drc error is due to an end near a reference segm end - if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - else // segments quelconques entre eux - { - // calcul de la "surface de securite du segment de reference - // First rought 'and fast) test : the track segment is like a rectangle - - m_xcliplo = m_ycliplo = -w_dist; - m_xcliphi = m_segmLength + w_dist; - m_ycliphi = w_dist; - - // A fine test is needed because a serment is not exactly a - // rectangle, it has rounded ends - if( !checkLine( segStartPoint, segEndPoint ) ) - { - /* 2eme passe : the track has rounded ends. - * we must a fine test for each rounded end and the - * rectangular zone - */ - - m_xcliplo = 0; - m_xcliphi = m_segmLength; - - if( !checkLine( segStartPoint, segEndPoint ) ) - { - wxPoint failurePoint; - - if( SegmentIntersectsSegment( aRefSeg->GetStart(), aRefSeg->GetEnd(), - track->GetStart(), track->GetEnd(), - &failurePoint ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACKS_CROSSING ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, failurePoint ); - addMarkerToPcb( marker ); - } - else - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - } - - if( !m_reportAllTrackErrors ) - return; - } - else // The drc error is due to the starting or the ending point of the reference segment - { - // Test the starting and the ending point - segStartPoint = track->GetStart(); - segEndPoint = track->GetEnd(); - delta = segEndPoint - segStartPoint; - - // Compute the segment orientation (angle) en 0,1 degre - double angle = ArcTangente( delta.y, delta.x ); - - // Compute the segment length: delta.x = length after rotation - RotatePoint( &delta, angle ); - - /* Comute the reference segment coordinates relatives to a - * X axis = current tested segment - */ - wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint; - wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint; - - RotatePoint( &relStartPos, angle ); - RotatePoint( &relEndPos, angle ); - - if( !checkMarginToCircle( relStartPos, w_dist, delta.x, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - - if( !checkMarginToCircle( relEndPos, w_dist, delta.x, &actual ) ) - { - DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_ENDS ); - drcItem->SetItems( aRefSeg, track ); - - MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, track, seg ) ); - addMarkerToPcb( marker ); - - if( !m_reportAllTrackErrors ) - return; - } - } - } + if( !m_reportAllTrackErrors ) + return; } } @@ -727,7 +482,7 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato // Can be *very* time consumming. if( aTestZones ) { - SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); + SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); for( ZONE_CONTAINER* zone : m_pcb->Zones() ) { @@ -737,21 +492,28 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato if( !( layerMask & zone->GetLayerSet() ).any() ) continue; - if( zone->GetNetCode() && zone->GetNetCode() == net_code_ref ) + if( zone->GetNetCode() && zone->GetNetCode() == refNetCode ) continue; - int clearance = std::max( ref_seg_clearance, zone->GetClearance() ); + int clearance = std::max( refSegClearance, zone->GetClearance() ); SHAPE_POLY_SET* outline = const_cast( &zone->GetFilledPolysList() ); - int error = clearance - outline->Distance( refSeg, ref_seg_width ); + int error = clearance - outline->Distance( testSeg, refSegWidth ); // 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( error > THRESHOLD_DIST ) { DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_ZONE ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), clearance, true ), + MessageTextFromValue( userUnits(), clearance - error, true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( aRefSeg, zone ); MARKER_PCB* marker = new MARKER_PCB( drcItem, getLocation( aRefSeg, zone ) ); @@ -764,42 +526,50 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato /* Phase 4: test DRC with to board edge */ /***********************************************/ { - SEG test_seg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); + SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); - int clearance = std::max( ref_seg_clearance, dsnSettings.m_CopperEdgeClearance ); - - // the minimum distance = clearance plus half the reference track width - SEG::ecoord w_dist = clearance + ref_seg_width / 2; - SEG::ecoord w_dist_sq = w_dist * w_dist; + int clearance = std::max( refSegClearance, dsnSettings.m_CopperEdgeClearance ); + int halfWidth = refSegWidth / 2; + int center2centerAllowed = clearance + halfWidth; for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ ) { - if( test_seg.SquaredDistance( *it ) < w_dist_sq ) + SEG::ecoord center2center_squared = testSeg.SquaredDistance( *it ); + + if( center2center_squared < square( center2centerAllowed ) ) { - VECTOR2I pt = test_seg.NearestPoint( *it ); + VECTOR2I pt = testSeg.NearestPoint( *it ); KICAD_T types[] = { PCB_LINE_T, EOT }; DRAWSEGMENT* edge = nullptr; - INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData ) - { - DRAWSEGMENT* test_edge = dynamic_cast( item ); + INSPECTOR_FUNC inspector = + [&] ( EDA_ITEM* item, void* testData ) + { + DRAWSEGMENT* test_edge = dynamic_cast( item ); - if( !test_edge || test_edge->GetLayer() != Edge_Cuts ) - return SEARCH_RESULT::CONTINUE; + if( !test_edge || test_edge->GetLayer() != Edge_Cuts ) + return SEARCH_RESULT::CONTINUE; - if( test_edge->HitTest((wxPoint) pt, w_dist ) ) - { - edge = test_edge; - return SEARCH_RESULT::QUIT; - } + if( test_edge->HitTest( (wxPoint) pt, clearance + halfWidth ) ) + { + edge = test_edge; + return SEARCH_RESULT::QUIT; + } - return SEARCH_RESULT::CONTINUE; - }; + return SEARCH_RESULT::CONTINUE; + }; // Best-efforts search for edge segment BOARD::IterateForward( m_pcb->Drawings(), inspector, nullptr, types ); + int actual = std::max( 0.0, sqrt( center2center_squared ) - halfWidth ); DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_EDGE ); + + msg.Printf( drcItem->GetErrorText() + _( "(minimum %s; actual %s)" ), + MessageTextFromValue( userUnits(), clearance, true ), + MessageTextFromValue( userUnits(), actual, true ) ); + + drcItem->SetErrorMessage( msg ); drcItem->SetItems( aRefSeg, edge ); MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) pt ); @@ -810,34 +580,21 @@ void DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterato } -bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, int* aActual ) +bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual ) { - int dist; - double pad_angle; - - // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad - int dist_min = aRefPad->GetClearance( aPad ); - int dist_extra = 0; - - *aAllowed = dist_min; - // relativePadPos is the aPad shape position relative to the aRefPad shape position wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos(); - dist = KiROUND( EuclideanNorm( relativePadPos ) ); + int center2center = KiROUND( EuclideanNorm( relativePadPos ) ); - // Quick test: Clearance is OK if the bounding circles are further away than "dist_min" - int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius(); - - if( delta >= dist_min ) + // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance + if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) return true; - /* Here, pads are near and DRC depend on the pad shapes - * We must compare distance using a fine shape analysis - * Because a circle or oval shape is the easier shape to test, try to have - * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. - * if aRefPad = TRAP. and aPad = RECT, also swap pads - * Swap aRefPad and aPad if needed + /* Here, pads are near and DRC depends on the pad shapes. We must compare distance using + * a fine shape analysis. + * Because a circle or oval shape is the easier shape to test, swap pads to have aRefPad be + * a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. If aRefPad = TRAPEZOID and aPad = RECT, also swap. */ bool swap_pads; swap_pads = false; @@ -878,46 +635,49 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in relativePadPos = -relativePadPos; } - // corners of aRefPad (used only for rect/roundrect/trap pad) - wxPoint polyref[4]; - // corners of aRefPad (used only for custom pad) - SHAPE_POLY_SET polysetref; - - // corners of aPad (used only for rect/roundrect/trap pad) - wxPoint polycompare[4]; - // corners of aPad (used only custom pad) - SHAPE_POLY_SET polysetcompare; - - /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL, - * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. - * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID, - * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID - */ bool diag = true; - switch( aRefPad->GetShape() ) + if( ( aRefPad->GetShape() == PAD_SHAPE_CIRCLE || aRefPad->GetShape() == PAD_SHAPE_OVAL ) ) { - case PAD_SHAPE_CIRCLE: - - /* One can use checkClearanceSegmToPad to test clearance - * aRefPad is like a track segment with a null length and a witdth = GetSize().x + /* 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. */ - m_segmLength = 0; - m_segmAngle = 0; + wxPoint refPadStart = aRefPad->GetPosition() + aRefPad->GetOffset(); + wxPoint refPadEnd = aRefPad->GetPosition() + aRefPad->GetOffset(); + int refPadWidth; - m_segmEnd.x = m_segmEnd.y = 0; + if( aRefPad->GetSize().x > aRefPad->GetSize().y ) + { + refPadWidth = aRefPad->GetSize().y; + refPadStart.x -= ( aRefPad->GetSize().x - refPadWidth ) / 2; + refPadEnd.x += ( aRefPad->GetSize().x - refPadWidth ) / 2; + } + else + { + refPadWidth = aRefPad->GetSize().x; + refPadStart.y -= ( aRefPad->GetSize().y - refPadWidth ) / 2; + refPadEnd.y += ( aRefPad->GetSize().y - refPadWidth ) / 2; + } - m_padToTestPos = relativePadPos; - diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min, aActual ); - break; + SEG refPadSeg( refPadStart, refPadEnd ); + diag = checkClearanceSegmToPad( refPadSeg, refPadWidth, aPad, aMinClearance, aActual ); + } + else + { + int dist_extra = 0; + + // corners of aRefPad (used only for rect/roundrect/trap pad) + wxPoint polyref[4]; + // corners of aRefPad (used only for custom pad) + SHAPE_POLY_SET polysetref; + // corners of aPad (used only for rect/roundrect/trap pad) + wxPoint polycompare[4]; + // corners of aPad (used only custom pad) + SHAPE_POLY_SET polysetcompare; - case PAD_SHAPE_TRAPEZOID: - case PAD_SHAPE_ROUNDRECT: - case PAD_SHAPE_CHAMFERED_RECT: - case PAD_SHAPE_RECT: - case PAD_SHAPE_CUSTOM: // pad_angle = pad orient relative to the aRefPad orient - pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); + double pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); NORMALIZE_ANGLE_POS( pad_angle ); if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT ) @@ -929,16 +689,13 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in } else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT ) { - auto board = aRefPad->GetBoard(); - int maxError = ARC_HIGH_DEF; + BOARD* board = aRefPad->GetBoard(); + int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF; - if( board ) - maxError = board->GetDesignSettings().m_MaxError; - - // The reference pad can be rotated. calculate the rotated - // coordinates ( note, the ref pad position is the origin of - // coordinates for this drc test) + // The reference pad can be rotated. Calculate the rotated coordinates. + // (note, the ref pad position is the origin of coordinates for this drc test) int padRadius = aRefPad->GetRoundRectCornerRadius(); + TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(), aRefPad->GetOrientation(), padRadius, aRefPad->GetChamferRectRatio(), @@ -948,16 +705,14 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in { polysetref.Append( aRefPad->GetCustomShapeAsPolygon() ); - // The reference pad can be rotated. calculate the rotated - // coordiantes ( note, the ref pad position is the origin of - // coordinates for this drc test) + // The reference pad can be rotated. Calculate the rotated coordinates. + // (note, the ref pad position is the origin of coordinates for this drc test) aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ), aRefPad->GetOrientation() ); } else { - // BuildPadPolygon has meaning for rect a trapeziod shapes - // and returns the 4 corners + // BuildPadPolygon has meaning for rect a trapeziod shapes and returns the 4 corners. aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); } @@ -977,16 +732,13 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in } else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT ) { - auto board = aRefPad->GetBoard(); - int maxError = ARC_HIGH_DEF; + BOARD* board = aRefPad->GetBoard(); + int maxError = board ? board->GetDesignSettings().m_MaxError : ARC_HIGH_DEF; - if( board ) - maxError = board->GetDesignSettings().m_MaxError; - - // The reference pad can be rotated. calculate the rotated - // coordinates ( note, the ref pad position is the origin of - // coordinates for this drc test) + // The pad to compare can be rotated. Calculate the rotated coordinates. + // ( note, the pad to compare position is the relativePadPos for this drc test) int padRadius = aPad->GetRoundRectCornerRadius(); + TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos, aPad->GetSize(), aPad->GetOrientation(), padRadius, aPad->GetChamferRectRatio(), @@ -996,9 +748,8 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in { polysetcompare.Append( aPad->GetCustomShapeAsPolygon() ); - // The pad to compare can be rotated. calculate the rotated - // coordinattes ( note, the pad to compare position - // is the relativePadPos for this drc test + // The pad to compare can be rotated. Calculate the rotated coordinates. + // ( note, the pad to compare position is the relativePadPos for this drc test) aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos, aPad->GetOrientation() ); } @@ -1018,8 +769,8 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in { const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 ); // And now test polygons: - if( !poly2polyDRC((wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), - polycompare, 4, dist_min + dist_extra, aActual ) ) + if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), + polycompare, 4, aMinClearance + dist_extra, aActual ) ) { *aActual = std::max( 0, *aActual - dist_extra ); diag = false; @@ -1030,7 +781,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 ); // And now test polygons: if( !poly2polyDRC((wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), - polyref, 4, dist_min + dist_extra, aActual ) ) + polyref, 4, aMinClearance + dist_extra, aActual ) ) { *aActual = std::max( 0, *aActual - dist_extra ); diag = false; @@ -1044,7 +795,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in // And now test polygons: if( !poly2polyDRC((wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), - dist_min + dist_extra, aActual ) ) + aMinClearance + dist_extra, aActual ) ) { *aActual = std::max( 0, *aActual - dist_extra ); diag = false; @@ -1052,7 +803,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in } else { - if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min + dist_extra, aActual ) ) + if( !poly2polyDRC( polyref, 4, polycompare, 4, aMinClearance + dist_extra, aActual ) ) { *aActual = std::max( 0, *aActual - dist_extra ); diag = false; @@ -1064,441 +815,97 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int* aAllowed, in wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() ); break; } - break; - - case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */ - { - /* Create a track segment with same dimensions as the oval aRefPad - * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance - */ - int segm_width; - m_segmAngle = aRefPad->GetOrientation(); // Segment orient. - - if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment - { - segm_width = aRefPad->GetSize().y; - m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y; - } - else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg - { - segm_width = aRefPad->GetSize().x; - m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x; - m_segmAngle += 900; - } - - /* the start point must be 0,0 and currently relativePadPos - * is relative the center of pad coordinate */ - wxPoint segstart; - segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment - - RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment - // Calculate segment end position relative to the segment origin - m_segmEnd.x = -2 * segstart.x; - m_segmEnd.y = -2 * segstart.y; - - // Recalculate the equivalent segment angle in 0,1 degrees - // to prepare a call to checkClearanceSegmToPad() - m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x ); - - // move pad position relative to the segment origin - m_padToTestPos = relativePadPos - segstart; - - // Use segment to pad check to test the second pad: - diag = checkClearanceSegmToPad( aPad, segm_width, dist_min, aActual ); - break; - } - - default: - wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) ); - break; } return diag; } -/* test if distance between a segment is > aMinDist - * segment start point is assumed in (0,0) and segment start point in m_segmEnd - * and its orientation is m_segmAngle (m_segmAngle must be already initialized) - * and have aSegmentWidth. +/* + * Test if distance between a segment and a pad is > minClearance. Return the actual + * distance if it is less. */ -bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist, - int* aActualDist ) +bool DRC::checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_PAD* pad, + int minClearance, int* aActualDist ) { - // Note: - // we are using a horizontal segment for test, because we know here - // only the length and orientation+ of the segment - // Therefore the coordinates of the shape of pad to compare - // must be calculated in a axis system rotated by m_segmAngle - // and centered to the segment origin, before they can be tested - // against the segment - // We are using: - // m_padToTestPos the position of the pad shape in this axis system - // m_segmAngle the axis system rotation - - int segmHalfWidth = aSegmentWidth / 2; - int distToLine = segmHalfWidth + aMinDist; - - wxSize padHalfsize; // half dimension of the pad - - if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) + if( ( pad->GetShape() == PAD_SHAPE_CIRCLE || pad->GetShape() == PAD_SHAPE_OVAL ) ) { - // For a custom pad, the pad size has no meaning, we only can - // use the bounding radius - padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius(); - } - else - { - padHalfsize = aPad->GetSize() / 2; - } - - if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size - { - padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change - padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change - } - - if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) - { - /* Easy case: just test the distance between segment and pad centre - * calculate pad coordinates in the X,Y axis with X axis = segment to test + /* 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. */ - RotatePoint( &m_padToTestPos, m_segmAngle ); + wxPoint padStart = pad->GetPosition() + pad->GetOffset(); + wxPoint padEnd = pad->GetPosition() + pad->GetOffset(); + int padHalfWidth; - if( !checkMarginToCircle( m_padToTestPos, aMinDist + segmHalfWidth + padHalfsize.x, - m_segmLength, aActualDist ) ) + if( pad->GetSize().x > pad->GetSize().y ) { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth - padHalfsize.x ); - return false; + padHalfWidth = pad->GetSize().y / 2; + padStart.x -= ( pad->GetSize().x / 2 ) - padHalfWidth; + padEnd.x += ( pad->GetSize().x / 2 ) - padHalfWidth; + } + else + { + padHalfWidth = pad->GetSize().x / 2; + padStart.y -= ( pad->GetSize().y / 2 ) - padHalfWidth; + padEnd.y += ( pad->GetSize().y / 2 ) - padHalfWidth; } - return true; - } + SEG padSeg( padStart, padEnd ); + int widths = padHalfWidth + ( refSegWidth / 2 ); + int center2centerAllowed = minClearance + widths; - /* calculate the bounding box of the pad, including the clearance and the segment width - * if the line from 0 to m_segmEnd does not intersect this bounding box, - * the clearance is always OK - * But if intersect, a better analysis of the pad shape must be done. - */ - m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x; - m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y; - m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x; - m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y; + // Avoid square-roots if possible (for performance) + SEG::ecoord center2center_squared = refSeg.SquaredDistance( padSeg ); - wxPoint startPoint( 0, 0 ); - wxPoint endPoint = m_segmEnd; - - double orient = aPad->GetOrientation(); - - RotatePoint( &startPoint, m_padToTestPos, -orient ); - RotatePoint( &endPoint, m_padToTestPos, -orient ); - - if( checkLine( startPoint, endPoint ) ) - return true; - - /* segment intersects the bounding box. But there is not always a DRC error. - * A fine analysis of the pad shape must be done. - */ - switch( aPad->GetShape() ) - { - case PAD_SHAPE_CIRCLE: - // This case was already tested, so it cannot be found here. - // it is here just to avoid compil warning, and to remember - // it is already tested. - return false; - - case PAD_SHAPE_OVAL: - { - /* an oval is a complex shape, but is a rectangle and 2 circles - * these 3 basic shapes are more easy to test. - * - * In calculations we are using a vertical or horizontal oval shape - * (i.e. a vertical or horizontal rounded segment) - */ - wxPoint cstart = m_padToTestPos; - wxPoint cend = m_padToTestPos; // center of each circle - int delta = std::abs( padHalfsize.y - padHalfsize.x ); - int radius = std::min( padHalfsize.y, padHalfsize.x ); - - if( padHalfsize.x > padHalfsize.y ) // horizontal equivalent segment + if( center2center_squared < square( center2centerAllowed ) ) { - cstart.x -= delta; - cend.x += delta; - // Build the rectangular clearance area between the two circles - // the rect starts at cstart.x and ends at cend.x and its height - // is (radius + distToLine)*2 - m_xcliplo = cstart.x; - m_ycliplo = cstart.y - radius - distToLine; - m_xcliphi = cend.x; - m_ycliphi = cend.y + radius + distToLine; - } - else // vertical equivalent segment - { - cstart.y -= delta; - cend.y += delta; - // Build the rectangular clearance area between the two circles - // the rect starts at cstart.y and ends at cend.y and its width - // is (radius + distToLine)*2 - m_xcliplo = cstart.x - distToLine - radius; - m_ycliplo = cstart.y; - m_xcliphi = cend.x + distToLine + radius; - m_ycliphi = cend.y; - } - - // Test the rectangular clearance area between the two circles (the rounded ends) - // If the segment legth is zero, only check the endpoints, skip the rectangle - if( m_segmLength && !checkLine( startPoint, endPoint ) ) - { - // JEY TODO: set aActual - return false; - } - - // test the first end - // Calculate the actual position of the circle, given the pad orientation: - RotatePoint( &cstart, m_padToTestPos, orient ); - - // Calculate the actual position of the circle in the new X,Y axis, relative - // to the segment: - RotatePoint( &cstart, m_segmAngle ); - - if( !checkMarginToCircle( cstart, aMinDist + radius + segmHalfWidth, m_segmLength, - aActualDist) ) - { - *aActualDist = std::max( 0, *aActualDist - radius - segmHalfWidth ); - return false; - } - - // test the second end - RotatePoint( &cend, m_padToTestPos, orient ); - RotatePoint( &cend, m_segmAngle ); - - if( !checkMarginToCircle( cend, aMinDist + radius + segmHalfWidth, m_segmLength, - aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - radius - segmHalfWidth ); + *aActualDist = std::max( 0.0, sqrt( center2center_squared ) - widths ); return false; } } - break; + else if( pad->GetShape() == PAD_SHAPE_RECT || pad->GetShape() == PAD_SHAPE_ROUNDRECT ) + { + wxSize padSize = pad->GetSize(); + int widths = refSegWidth / 2; - case PAD_SHAPE_ROUNDRECT: + // 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 ) { - // a round rect is a smaller rect, with a clearance augmented by the corners radius - int r = aPad->GetRoundRectCornerRadius(); - padHalfsize.x -= r; - padHalfsize.y -= r; - distToLine += r; + padSize.x -= 2 * pad->GetRoundRectCornerRadius(); + padSize.y -= 2 * pad->GetRoundRectCornerRadius(); + widths += pad->GetRoundRectCornerRadius(); } - KI_FALLTHROUGH; - case PAD_SHAPE_RECT: - // the area to test is a rounded rectangle. - // this can be done by testing 2 rectangles and 4 circles (the corners) + wxPoint padStart = pad->GetPosition() + pad->GetOffset() - ( padSize / 2 ); + SHAPE_RECT padShape( padStart, padSize.x, padSize.y ); + int actual; - // Testing the first rectangle dimx + distToLine, dimy: - m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine; - m_ycliplo = m_padToTestPos.y - padHalfsize.y; - m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine; - m_ycliphi = m_padToTestPos.y + padHalfsize.y; - - if( !checkLine( startPoint, endPoint ) ) + if( padShape.DoCollide( refSeg, minClearance + widths, &actual ) ) { - // JEY TODO: set aActual + *aActualDist = actual - widths; return false; } - - // Testing the second rectangle dimx , dimy + distToLine - m_xcliplo = m_padToTestPos.x - padHalfsize.x; - m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine; - m_xcliphi = m_padToTestPos.x + padHalfsize.x; - m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine; - - if( !checkLine( startPoint, endPoint ) ) - { - // JEY TODO: set aActual - return false; - } - - // testing the 4 circles which are the clearance area of each corner: - - // testing the left top corner of the rectangle - startPoint.x = m_padToTestPos.x - padHalfsize.x; - startPoint.y = m_padToTestPos.y - padHalfsize.y; - RotatePoint( &startPoint, m_padToTestPos, orient ); - RotatePoint( &startPoint, m_segmAngle ); - - if( !checkMarginToCircle( startPoint, aMinDist + segmHalfWidth, m_segmLength, - aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - - // testing the right top corner of the rectangle - startPoint.x = m_padToTestPos.x + padHalfsize.x; - startPoint.y = m_padToTestPos.y - padHalfsize.y; - RotatePoint( &startPoint, m_padToTestPos, orient ); - RotatePoint( &startPoint, m_segmAngle ); - - if( !checkMarginToCircle( startPoint, aMinDist + segmHalfWidth, m_segmLength, - aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - - // testing the left bottom corner of the rectangle - startPoint.x = m_padToTestPos.x - padHalfsize.x; - startPoint.y = m_padToTestPos.y + padHalfsize.y; - RotatePoint( &startPoint, m_padToTestPos, orient ); - RotatePoint( &startPoint, m_segmAngle ); - - if( !checkMarginToCircle( startPoint, aMinDist + segmHalfWidth, m_segmLength, - aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - - // testing the right bottom corner of the rectangle - startPoint.x = m_padToTestPos.x + padHalfsize.x; - startPoint.y = m_padToTestPos.y + padHalfsize.y; - RotatePoint( &startPoint, m_padToTestPos, orient ); - RotatePoint( &startPoint, m_segmAngle ); - - if( !checkMarginToCircle( startPoint, aMinDist + segmHalfWidth, m_segmLength, - aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - - break; - - case PAD_SHAPE_TRAPEZOID: - { - wxPoint poly[4]; - aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient ); - - // Move shape to m_padToTestPos - for( int ii = 0; ii < 4; ii++ ) - { - poly[ii] += m_padToTestPos; - RotatePoint( &poly[ii], m_segmAngle ); - } - - if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ), wxPoint( m_segmLength, 0 ), - aMinDist + segmHalfWidth, aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - } - break; - - case PAD_SHAPE_CUSTOM: - { + } + else // Convert the rest to polygons + { SHAPE_POLY_SET polyset; - polyset.Append( aPad->GetCustomShapeAsPolygon() ); - // The pad can be rotated. calculate the coordinates - // relatives to the segment being tested - // Note, the pad position relative to the segment origin - // is m_padToTestPos - aPad->CustomShapeAsPolygonToBoardPosition( &polyset, m_padToTestPos, orient ); - // Rotate all coordinates by m_segmAngle, because the segment orient - // is m_segmAngle - // we are using a horizontal segment for test, because we know here - // only the lenght and orientation+ of the segment - // therefore all coordinates of the pad to test must be rotated by - // m_segmAngle (they are already relative to the segment origin) - aPad->CustomShapeAsPolygonToBoardPosition( &polyset, wxPoint( 0, 0 ), m_segmAngle ); + 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( 0, 0 ), wxPoint(m_segmLength,0), - aMinDist + segmHalfWidth, aActualDist ) ) + (wxPoint) refSeg.A, (wxPoint) refSeg.B, + minClearance + widths, &actual ) ) { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - } - break; - - case PAD_SHAPE_CHAMFERED_RECT: - { - auto board = aPad->GetBoard(); - int maxError = ARC_HIGH_DEF; - - if( board ) - maxError = board->GetDesignSettings().m_MaxError; - - SHAPE_POLY_SET polyset; - // The pad can be rotated. calculate the coordinates - // relatives to the segment being tested - // Note, the pad position relative to the segment origin - // is m_padToTestPos - int padRadius = aPad->GetRoundRectCornerRadius(); - TransformRoundChamferedRectToPolygon( polyset, m_padToTestPos, aPad->GetSize(), - aPad->GetOrientation(), - padRadius, aPad->GetChamferRectRatio(), - aPad->GetChamferPositions(), maxError ); - // Rotate also coordinates by m_segmAngle, because the segment orient - // is m_segmAngle. - // we are using a horizontal segment for test, because we know here - // only the lenght and orientation of the segment - // therefore all coordinates of the pad to test must be rotated by - // m_segmAngle (they are already relative to the segment origin) - polyset.Rotate( DECIDEG2RAD( -m_segmAngle ), VECTOR2I( 0, 0 ) ); - - const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 ); - - if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), - wxPoint( 0, 0 ), wxPoint(m_segmLength,0), - aMinDist + segmHalfWidth, aActualDist ) ) - { - *aActualDist = std::max( 0, *aActualDist - segmHalfWidth ); - return false; - } - } - break; - } - - return true; -} - - -/** - * Helper function checkMarginToCircle - * Check the distance between a circle (round pad, via or round end of track) - * and a segment. the segment is expected starting at 0,0, and on the X axis - * return true if distance >= aAllowed - */ -bool DRC::checkMarginToCircle( wxPoint aCentre, int aAllowed, int aLength, int* aActual ) -{ - if( abs( aCentre.y ) >= aAllowed ) // trivial case - return true; - - // Here, distance between aCentre and X axis is < aAllowed - if( ( aCentre.x > -aAllowed ) && ( aCentre.x < ( aLength + aAllowed ) ) ) - { - if( ( aCentre.x >= 0 ) && ( aCentre.x <= aLength ) ) - { - // aCentre is between the starting point and the ending point of the segm - *aActual = abs( aCentre.y ); - return false; - } - - if( aCentre.x > aLength ) // aCentre is after the ending point - aCentre.x -= aLength; // move aCentre to the starting point of the segment - - int distToOrigin = KiROUND( EuclideanNorm( aCentre ) ); - - if( distToOrigin < aAllowed ) - { - // distance between aCentre and the starting point or the ending point is < aAllowed - *aActual = distToOrigin; + *aActualDist = actual - widths; return false; } } @@ -1506,168 +913,3 @@ bool DRC::checkMarginToCircle( wxPoint aCentre, int aAllowed, int aLength, int* return true; } - -// Helper function used in checkLine:: -static inline int USCALE( unsigned arg, unsigned num, unsigned den ) -{ - int ii; - double result; - - // Trivial check first - if( !arg || !num) - return 0; - - // If arg and num are both non-zero but den is zero, we return effective infinite - if( !den ) - return INT_MAX; - - result = ( (double) arg * num ) / den; - - // Ensure that our result doesn't overflow into the sign bit - if( result > INT_MAX ) - return INT_MAX; - - ii = KiROUND( ( (double) arg * num ) / den ); - return ii; -} - - -/** Helper function checkLine - * Test if a line intersects a bounding box (a rectangle) - * The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi - * return true if the line from aSegStart to aSegEnd is outside the bounding box - */ -bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd ) -{ -#define WHEN_OUTSIDE return true -#define WHEN_INSIDE - int temp; - - if( aSegStart.x > aSegEnd.x ) - std::swap( aSegStart, aSegEnd ); - - if( (aSegEnd.x <= m_xcliplo) || (aSegStart.x >= m_xcliphi) ) - { - WHEN_OUTSIDE; - } - - if( aSegStart.y < aSegEnd.y ) - { - if( (aSegEnd.y <= m_ycliplo) || (aSegStart.y >= m_ycliphi) ) - { - WHEN_OUTSIDE; - } - - if( aSegStart.y < m_ycliplo ) - { - temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y), - (aSegEnd.y - aSegStart.y) ); - - if( (aSegStart.x += temp) >= m_xcliphi ) - { - WHEN_OUTSIDE; - } - - aSegStart.y = m_ycliplo; - WHEN_INSIDE; - } - - if( aSegEnd.y > m_ycliphi ) - { - temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi), - (aSegEnd.y - aSegStart.y) ); - - if( (aSegEnd.x -= temp) <= m_xcliplo ) - { - WHEN_OUTSIDE; - } - - aSegEnd.y = m_ycliphi; - WHEN_INSIDE; - } - - if( aSegStart.x < m_xcliplo ) - { - temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x), - (aSegEnd.x - aSegStart.x) ); - aSegStart.y += temp; - aSegStart.x = m_xcliplo; - WHEN_INSIDE; - } - - if( aSegEnd.x > m_xcliphi ) - { - temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi), - (aSegEnd.x - aSegStart.x) ); - aSegEnd.y -= temp; - aSegEnd.x = m_xcliphi; - WHEN_INSIDE; - } - } - else - { - if( (aSegStart.y <= m_ycliplo) || (aSegEnd.y >= m_ycliphi) ) - { - WHEN_OUTSIDE; - } - - if( aSegStart.y > m_ycliphi ) - { - temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi), - (aSegStart.y - aSegEnd.y) ); - - if( (aSegStart.x += temp) >= m_xcliphi ) - { - WHEN_OUTSIDE; - } - - aSegStart.y = m_ycliphi; - WHEN_INSIDE; - } - - if( aSegEnd.y < m_ycliplo ) - { - temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y), - (aSegStart.y - aSegEnd.y) ); - - if( (aSegEnd.x -= temp) <= m_xcliplo ) - { - WHEN_OUTSIDE; - } - - aSegEnd.y = m_ycliplo; - WHEN_INSIDE; - } - - if( aSegStart.x < m_xcliplo ) - { - temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x), - (aSegEnd.x - aSegStart.x) ); - aSegStart.y -= temp; - aSegStart.x = m_xcliplo; - WHEN_INSIDE; - } - - if( aSegEnd.x > m_xcliphi ) - { - temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi), - (aSegEnd.x - aSegStart.x) ); - aSegEnd.y += temp; - aSegEnd.x = m_xcliphi; - WHEN_INSIDE; - } - } - - // Do not divide here to avoid rounding errors - if( ( (aSegEnd.x + aSegStart.x) < m_xcliphi * 2 ) - && ( (aSegEnd.x + aSegStart.x) > m_xcliplo * 2) \ - && ( (aSegEnd.y + aSegStart.y) < m_ycliphi * 2 ) - && ( (aSegEnd.y + aSegStart.y) > m_ycliplo * 2 ) ) - { - return false; - } - else - { - return true; - } -}