From 0264501676ecf7329d6a857d7bcf023db5d85c2c Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 26 Nov 2018 11:10:14 -0800 Subject: [PATCH] drc: Add board outline and edge crossing This adds a check for contiguous board outlines to the DRC. It also uses the calculated outline to ensure that traces are not crossing the outlines. Fixes: lp:1648055 * https://bugs.launchpad.net/kicad/+bug/1648055 --- pcbnew/class_board.cpp | 16 +++++++++---- pcbnew/class_board.h | 9 +++++-- pcbnew/class_module.cpp | 2 +- .../convert_drawsegment_list_to_polygon.cpp | 19 ++++++++++++--- pcbnew/dialogs/dialog_export_step.cpp | 3 ++- pcbnew/drc.cpp | 20 +++++++++++++++- pcbnew/drc.h | 9 +++++++ pcbnew/drc_clearance_test_functions.cpp | 24 +++++++++++++++++++ pcbnew/drc_item.cpp | 4 ++++ 9 files changed, 93 insertions(+), 13 deletions(-) diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 05c219008..66b8488b9 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -171,8 +171,6 @@ void BOARD::BuildConnectivity() const wxPoint BOARD::GetPosition() const { - wxLogWarning( wxT( "This should not be called on the BOARD object") ); - return ZeroOffset; } @@ -1021,6 +1019,12 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem ) } +wxString BOARD::GetSelectMenuText( EDA_UNITS_T aUnits ) const +{ + return wxString::Format( _( "PCB" ) ); +} + + void BOARD::DeleteMARKERs() { // the vector does not know how to delete the MARKER_PCB, it holds pointers @@ -2949,12 +2953,14 @@ BOARD_ITEM* BOARD::Duplicate( const BOARD_ITEM* aItem, * return true if success, false if a contour is not valid */ extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, - wxString* aErrorText, unsigned int aTolerance ); + wxString* aErrorText, unsigned int aTolerance, + wxPoint* aErrorLocation = nullptr ); -bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText ) +bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText, wxPoint* aErrorLocation ) { - bool success = BuildBoardPolygonOutlines( this, aOutlines, aErrorText, Millimeter2iu( 0.05 ) ); + bool success = BuildBoardPolygonOutlines( this, aOutlines, aErrorText, + Millimeter2iu( 0.05 ), aErrorLocation ); // Make polygon strictly simple to avoid issues (especially in 3D viewer) aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 7074abf49..076155639 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -562,6 +562,8 @@ public: const ZONE_SETTINGS& GetZoneSettings() const { return m_zoneSettings; } void SetZoneSettings( const ZONE_SETTINGS& aSettings ) { m_zoneSettings = aSettings; } + wxString GetSelectMenuText( EDA_UNITS_T aUnits ) const override; + /** * Function GetColorSettings * @return the current COLORS_DESIGN_SETTINGS in use @@ -585,11 +587,14 @@ public: * @param aOutlines The SHAPE_POLY_SET to fill in with outlines/holes. * @param aErrorText = a wxString reference to display an error message * with the coordinate of the point which creates the error - * (default = NULL , no message returned on error) + * (default = nullptr , no message returned on error) + * @param aErrorLocation = a wxPoint giving the location of the Error message on the board + * if left null (default), no location is returned + * * @return true if success, false if a contour is not valid */ bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, - wxString* aErrorText = NULL ); + wxString* aErrorText = nullptr, wxPoint* aErrorLocation = nullptr ); /** * Function ConvertBrdLayerToPolygonalContours diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 9f658e60e..17a1b5b73 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -1445,7 +1445,7 @@ double MODULE::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const // see convert_drawsegment_list_to_polygon.cpp: extern bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& aPolygons, - wxString* aErrorText, unsigned int aTolerance ); + wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation = nullptr ); bool MODULE::BuildPolyCourtyard() { diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_drawsegment_list_to_polygon.cpp index d39904cb3..ac73a27b9 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.cpp +++ b/pcbnew/convert_drawsegment_list_to_polygon.cpp @@ -177,9 +177,10 @@ static DRAWSEGMENT* findPoint( const wxPoint& aPoint, std::vector< DRAWSEGMENT* * @param aPolygons will contain the complex polygon. * @param aTolerance is the max distance between points that is still accepted as connected (internal units) * @param aErrorText is a wxString to return error message. + * @param aErrorLocation is the optional position of the error in the outline */ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& aPolygons, - wxString* aErrorText, unsigned int aTolerance ) + wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation ) { if( aSegList.size() == 0 ) return true; @@ -415,6 +416,9 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SE *aErrorText << msg << "\n"; } + if( aErrorLocation ) + *aErrorLocation = graphic->GetPosition(); + return false; } @@ -443,6 +447,9 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SE *aErrorText << msg << "\n"; } + if( aErrorLocation ) + *aErrorLocation = prevPt; + return false; } break; @@ -594,6 +601,9 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SE *aErrorText << msg << "\n"; } + if( aErrorLocation ) + *aErrorLocation = graphic->GetPosition(); + return false; } @@ -622,6 +632,9 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SE *aErrorText << msg << "\n"; } + if( aErrorLocation ) + *aErrorLocation = prevPt; + return false; } break; @@ -641,7 +654,7 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SE * All contours should be closed, i.e. valid closed polygon vertices */ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, - wxString* aErrorText, unsigned int aTolerance ) + wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation ) { PCB_TYPE_COLLECTOR items; @@ -659,7 +672,7 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) ); } - bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance ); + bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance, aErrorLocation ); if( !success || !aOutlines.OutlineCount() ) { diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index e198ccd9d..550fdf4a6 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/pcbnew/dialogs/dialog_export_step.cpp @@ -221,7 +221,8 @@ void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent ) } extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, - wxString* aErrorText, unsigned int aTolerance ); + wxString* aErrorText, unsigned int aTolerance, + wxPoint* aErrorLocation = nullptr ); void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent ) { diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 2c3e8be89..e0aced286 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -393,8 +393,15 @@ void DRC::RunTests( wxTextCtrl* aMessages ) // ( the board can be reloaded ) m_pcb = m_pcbEditorFrame->GetBoard(); - // someone should have cleared the two lists before calling this. + if( aMessages ) + { + aMessages->AppendText( _( "Board Outline...\n" ) ); + wxSafeYield(); + } + + testOutline(); + // someone should have cleared the two lists before calling this. if( !testNetClasses() ) { // testing the netclasses is a special case because if the netclasses @@ -1165,6 +1172,17 @@ void DRC::testCopperTextItem( BOARD_ITEM* aTextItem ) } +void DRC::testOutline() +{ + wxPoint error_loc( m_pcb->GetBoardEdgesBoundingBox().GetPosition() ); + if( !m_pcb->GetBoardPolygonOutlines( m_board_outlines, nullptr, &error_loc ) ) + { + addMarkerToPcb( newMarker( error_loc, m_pcb, DRCE_INVALID_OUTLINE ) ); + return; + } +} + + void DRC::testDisabledLayers() { BOARD* board = m_pcbEditorFrame->GetBoard(); diff --git a/pcbnew/drc.h b/pcbnew/drc.h index ffd378de6..b20600c9c 100644 --- a/pcbnew/drc.h +++ b/pcbnew/drc.h @@ -32,6 +32,7 @@ #include #include #include +#include #define OK_DRC 0 #define BAD_DRC 1 @@ -94,6 +95,8 @@ #define DRCE_BURIED_VIA_NOT_ALLOWED 49 ///< buried vias are not allowed #define DRCE_DISABLED_LAYER_ITEM 50 ///< item on a disabled layer #define DRCE_DRILLED_HOLES_TOO_CLOSE 51 ///< overlapping drilled holes break drill bits +#define DRCE_TRACK_NEAR_EDGE 53 ///< track too close to board edge +#define DRCE_INVALID_OUTLINE 54 ///< invalid board outline class EDA_DRAW_PANEL; @@ -220,6 +223,7 @@ private: 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 DIALOG_DRC_CONTROL* m_drcDialog; DRC_LIST m_unconnected; ///< list of unconnected pads, as DRC_ITEMs @@ -314,6 +318,11 @@ private: ///> Tests for items placed on disabled layers (causing false connections). void testDisabledLayers(); + /** + * Test that the board outline is contiguous and composed of valid elements + */ + void testOutline(); + //---------------------------------------------- bool doNetClass( const std::shared_ptr& aNetClass, wxString& msg ); diff --git a/pcbnew/drc_clearance_test_functions.cpp b/pcbnew/drc_clearance_test_functions.cpp index aac4a5387..0255549c5 100644 --- a/pcbnew/drc_clearance_test_functions.cpp +++ b/pcbnew/drc_clearance_test_functions.cpp @@ -704,6 +704,30 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads ) addMarkerToPcb( newMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE ) ); } + /***********************************************/ + /* Phase 4: test DRC with to board edge */ + /***********************************************/ + { + SEG test_seg( aRefSeg->GetStart(), aRefSeg->GetEnd() ); + + // the minimum distance = clearance plus half the reference track + // width plus half the other track's width + int w_dist = aRefSeg->GetClearance( aRefSeg ); + w_dist += aRefSeg->GetWidth() / 2; + + for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ ) + { + if( auto pt = test_seg.Intersect( *it, false, false ) ) + { + markers.push_back( newMarker( wxPoint( pt->x, pt->y ), aRefSeg, DRCE_TRACK_NEAR_EDGE ) ); + + if( !handleNewMarker() ) + return false; + } + } + } + + if( markers.size() > 0 ) { commitMarkers(); diff --git a/pcbnew/drc_item.cpp b/pcbnew/drc_item.cpp index 6c7101047..1b5f0adb7 100644 --- a/pcbnew/drc_item.cpp +++ b/pcbnew/drc_item.cpp @@ -101,6 +101,10 @@ wxString DRC_ITEM::GetErrorText() const return wxString( _( "Micro via drill too small" ) ); case DRCE_DRILLED_HOLES_TOO_CLOSE: return wxString( _( "Drilled holes too close together" ) ); + case DRCE_TRACK_NEAR_EDGE: + return wxString( _( "Track too close to board edge" ) ); + case DRCE_INVALID_OUTLINE: + return wxString( _( "Board outline does not form a closed polygon" ) ); // use < since this is text ultimately embedded in HTML case DRCE_NETCLASS_TRACKWIDTH: -- 2.19.2