From b123318cf696594c48ad37e5b54f99c199ba2d6f Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 20 Nov 2020 13:55:10 +0000 Subject: [PATCH] Encourage use of full DRC for board & footprint malformed outlines. It shows much more detail. Removes some nag dialogs and places hypertext links in others. Also fixes the auto-layer-showing to correctly show Edge.Cuts or F.CrtYd or B.CrtYd for errors relating to them. Fixes https://gitlab.com/kicad/code/kicad/issues/6446 --- 3d-viewer/3d_canvas/board_adapter.cpp | 15 +++--- include/eda_item.h | 5 +- pcbnew/autorouter/ar_autoplacer.cpp | 2 +- pcbnew/board.cpp | 4 +- pcbnew/board.h | 4 +- .../convert_drawsegment_list_to_polygon.cpp | 41 +++------------ pcbnew/convert_drawsegment_list_to_polygon.h | 10 ++-- .../dialogs/dialog_constraints_reporter.cpp | 8 ++- pcbnew/dialogs/dialog_drc.cpp | 50 +++++++++++++------ pcbnew/dialogs/dialog_export_step.cpp | 8 +-- .../drc_test_provider_courtyard_clearance.cpp | 36 ++++++++++--- pcbnew/drc/drc_test_provider_misc.cpp | 47 ++++++++++------- pcbnew/exporters/export_hyperlynx.cpp | 1 + pcbnew/exporters/export_vrml.cpp | 17 ++----- pcbnew/exporters/gerber_placefile_writer.cpp | 10 ++-- pcbnew/footprint.cpp | 27 ++++------ pcbnew/footprint.h | 7 +-- pcbnew/pcb_expr_evaluator.cpp | 2 +- .../specctra_export.cpp | 9 ++-- pcbnew/tools/pcb_inspection_tool.cpp | 16 +++++- 20 files changed, 173 insertions(+), 146 deletions(-) diff --git a/3d-viewer/3d_canvas/board_adapter.cpp b/3d-viewer/3d_canvas/board_adapter.cpp index a766470938..2145fb6d18 100644 --- a/3d-viewer/3d_canvas/board_adapter.cpp +++ b/3d-viewer/3d_canvas/board_adapter.cpp @@ -461,27 +461,26 @@ bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg ) { m_board_poly.RemoveAllContours(); - bool success = false; - wxString msg; + bool success; if( m_board->IsFootprintHolder() ) { success = BuildFootprintPolygonOutlines( m_board, m_board_poly, - m_board->GetDesignSettings().m_MaxError, &msg ); + m_board->GetDesignSettings().m_MaxError, nullptr ); // Make polygon strictly simple to avoid issues (especially in 3D viewer) m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - if( aErrorMsg ) - *aErrorMsg = msg; + if( !success && aErrorMsg ) + *aErrorMsg = _( "Footprint outline is malformed. Run DRC for a full analysis." ); } else { - success = m_board->GetBoardPolygonOutlines( m_board_poly, &msg ); + success = m_board->GetBoardPolygonOutlines( m_board_poly ); - if( aErrorMsg ) - *aErrorMsg = _( "Board outline is not closed:" ) + wxS( " " )+ msg; + if( !success && aErrorMsg ) + *aErrorMsg = _( "Board outline is malformed. Run DRC for a full analysis." ); } return success; diff --git a/include/eda_item.h b/include/eda_item.h index ac531b7134..426aa2f355 100644 --- a/include/eda_item.h +++ b/include/eda_item.h @@ -119,8 +119,9 @@ typedef const INSPECTOR_FUNC& INSPECTOR; /// std::function passed to nested u #define IS_PASTED (1 << 17) ///< Modifier on IS_NEW which indicates it came from clipboard #define TRACK_LOCKED (1 << 18) ///< Pcbnew: track locked: protected from global deletion #define TRACK_AR (1 << 19) ///< Pcbnew: autorouted track -#define OBSOLETE_1 (1 << 20) ///< Not presently used -#define MALFORMED_COURTYARD (1 << 21) +#define MALFORMED_F_COURTYARD (1 << 20) +#define MALFORMED_B_COURTYARD (1 << 21) +#define MALFORMED_COURTYARDS ( MALFORMED_F_COURTYARD | MALFORMED_B_COURTYARD ) #define BEGIN_ONPAD (1 << 22) ///< Pcbnew: flag set for track segment starting on a pad #define END_ONPAD (1 << 23) ///< Pcbnew: flag set for track segment ending on a pad #define HOLE_PROXY (1 << 24) ///< Indicates the BOARD_ITEM is a proxy for its hole diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index 6c8a61ba56..5f28fccb81 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -113,7 +113,7 @@ int AR_AUTOPLACER::genPlacementRoutingMatrix() return 0; // Build the board shape - m_board->GetBoardPolygonOutlines( m_boardShape /*, aErrorText, aErrorLocation*/ ); + m_board->GetBoardPolygonOutlines( m_boardShape ); m_topFreeArea = m_boardShape; m_bottomFreeArea = m_boardShape; diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index 0ad76b0339..9ad0d12e9a 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -1842,12 +1842,12 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE* aCurr } -bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText, +bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, std::vector* aDiscontinuities, std::vector* aIntersections ) { bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError, - aErrorText, aDiscontinuities, aIntersections ); + aDiscontinuities, aIntersections ); // Make polygon strictly simple to avoid issues (especially in 3D viewer) aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); diff --git a/pcbnew/board.h b/pcbnew/board.h index 9aa2c4d3cb..58166c4ee8 100644 --- a/pcbnew/board.h +++ b/pcbnew/board.h @@ -588,8 +588,6 @@ public: * Any closed outline inside the main outline is a hole * All contours should be closed, i.e. have valid vertices to build a closed polygon * @param aOutlines The SHAPE_POLY_SET to fill in with outlines/holes. - * @param aErrorText = an optional wxString for an error message with the coordinate of the - * first discontinuity or self-intersection * @param aDiscontinuities = an optional array of wxPoint giving the locations of * discontinuities in the outline * @param aIntersections = an optional array of wxPoint giving the locations of self- @@ -597,7 +595,7 @@ public: * * @return true if success, false if a contour is not valid */ - bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText = nullptr, + bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, std::vector* aDiscontinuities = nullptr, std::vector* aIntersections = nullptr ); diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_drawsegment_list_to_polygon.cpp index 80c9a3c427..52cb881590 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.cpp +++ b/pcbnew/convert_drawsegment_list_to_polygon.cpp @@ -191,17 +191,14 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint, * These closed inner outlines are considered as holes in the main outline * @param aSegList the initial list of drawsegments (only lines, circles and arcs). * @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 aTolerance is the max error distance when polygonizing a curve (internal units) * @param aDiscontinuities = an optional array of wxPoint giving the locations of * discontinuities in the outline * @param aIntersections = an optional array of wxPoint giving the locations of self- * intersections in the outline */ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& aPolygons, - unsigned int aTolerance, wxString* aErrorText, - std::vector* aDiscontinuities, + int aTolerance, std::vector* aDiscontinuities, std::vector* aIntersections ) { if( aSegList.size() == 0 ) @@ -519,15 +516,6 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& } else // encountered discontinuity { - if( aErrorText ) - { - msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ), - StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x ), - StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y ) ); - - *aErrorText << msg << "\n"; - } - if( aDiscontinuities ) aDiscontinuities->emplace_back( prevPt ); @@ -731,15 +719,6 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& } else // encountered discontinuity { - if( aErrorText ) - { - msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ), - StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x ), - StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y ) ); - - *aErrorText << msg << "\n"; - } - if( aDiscontinuities ) aDiscontinuities->emplace_back( prevPt ); @@ -795,8 +774,8 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& * Any closed outline inside the main outline is a hole * All contours should be closed, i.e. valid closed polygon vertices */ -bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, unsigned int aTolerance, - wxString* aErrorText, std::vector* aDiscontinuities, +bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance, + std::vector* aDiscontinuities, std::vector* aIntersections ) { PCB_TYPE_COLLECTOR items; @@ -817,12 +796,8 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, unsign if( segList.size() ) { - success = ConvertOutlineToPolygon( segList, aOutlines, aTolerance, aErrorText, - aDiscontinuities, aIntersections ); - } - else if( aErrorText ) - { - *aErrorText = _( "No edges found on Edge.Cuts layer." ); + success = ConvertOutlineToPolygon( segList, aOutlines, aTolerance, aDiscontinuities, + aIntersections ); } if( !success || !aOutlines.OutlineCount() ) @@ -1035,8 +1010,8 @@ bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, segList.push_back( static_cast( items[ii] ) ); } - bool success = ConvertOutlineToPolygon( segList, outlines, aTolerance, aErrorText, - aDiscontinuities, aIntersections ); + bool success = ConvertOutlineToPolygon( segList, outlines, aTolerance, aDiscontinuities, + aIntersections ); FOOTPRINT* footprint = aBoard->GetFirstFootprint(); diff --git a/pcbnew/convert_drawsegment_list_to_polygon.h b/pcbnew/convert_drawsegment_list_to_polygon.h index 2c47d1bb85..be0b635ace 100644 --- a/pcbnew/convert_drawsegment_list_to_polygon.h +++ b/pcbnew/convert_drawsegment_list_to_polygon.h @@ -41,17 +41,14 @@ class wxPoint; * These closed inner outlines are considered as holes in the main outline * @param aSegList the initial list of drawsegments (only lines, circles and arcs). * @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 aTolerance is the max error distance when polygonizing a curve (internal units) * @param aDiscontinuities = an optional array of wxPoint giving the locations of * discontinuities in the outline * @param aIntersections = an optional array of wxPoint giving the locations of self- * intersections in the outline */ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& aPolygons, - unsigned int aTolerance, wxString* aErrorText, - std::vector* aDiscontinuities = nullptr, + int aTolerance, std::vector* aDiscontinuities = nullptr, std::vector* aIntersections = nullptr ); @@ -62,8 +59,7 @@ bool ConvertOutlineToPolygon( std::vector& aSegList, SHAPE_POLY_SET& * All contours should be closed, i.e. are valid vertices for a closed polygon * return true if success, false if a contour is not valid */ -extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, - unsigned int aTolerance, wxString* aErrorText, +extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance, std::vector* aDiscontinuities = nullptr, std::vector* aIntersections = nullptr ); diff --git a/pcbnew/dialogs/dialog_constraints_reporter.cpp b/pcbnew/dialogs/dialog_constraints_reporter.cpp index ef642fbbea..2de40eb4b0 100644 --- a/pcbnew/dialogs/dialog_constraints_reporter.cpp +++ b/pcbnew/dialogs/dialog_constraints_reporter.cpp @@ -23,8 +23,9 @@ #include #include +#include #include - +#include DIALOG_CONSTRAINTS_REPORTER::DIALOG_CONSTRAINTS_REPORTER( PCB_EDIT_FRAME* aParent ) : DIALOG_CONSTRAINTS_REPORTER_BASE( aParent ), @@ -47,7 +48,10 @@ void DIALOG_CONSTRAINTS_REPORTER::DeleteAllPages() void DIALOG_CONSTRAINTS_REPORTER::OnErrorLinkClicked( wxHtmlLinkEvent& event ) { - m_frame->ShowBoardSetupDialog( _( "Rules" ) ); + if( event.GetLinkInfo().GetHref() == "boardsetup" ) + m_frame->ShowBoardSetupDialog( _( "Rules" ) ); + else if( event.GetLinkInfo().GetHref() == "drc" ) + m_frame->GetToolManager()->RunAction( PCB_ACTIONS::runDRC, true ); } diff --git a/pcbnew/dialogs/dialog_drc.cpp b/pcbnew/dialogs/dialog_drc.cpp index 47ee996392..92ec6199b2 100644 --- a/pcbnew/dialogs/dialog_drc.cpp +++ b/pcbnew/dialogs/dialog_drc.cpp @@ -306,26 +306,46 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent ) if( item && node ) { PCB_LAYER_ID principalLayer = item->GetLayer(); - std::shared_ptr rc_item = node->m_RcItem; + LSET violationLayers; + std::shared_ptr rc_item = node->m_RcItem; - BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() ); - BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() ); - BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() ); - BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() ); + if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD ) + { + BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() ); - LSET violationLayers; + if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0 + && ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 ) + { + principalLayer = B_CrtYd; + } + else + { + principalLayer = F_CrtYd; + } + } + else if (rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE ) + { + principalLayer = Edge_Cuts; + } + else + { + BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() ); + BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() ); + BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() ); + BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() ); - if( a ) - violationLayers &= a->GetLayerSet(); + if( a ) + violationLayers &= a->GetLayerSet(); - if( b ) - violationLayers &= b->GetLayerSet(); + if( b ) + violationLayers &= b->GetLayerSet(); - if( c ) - violationLayers &= c->GetLayerSet(); + if( c ) + violationLayers &= c->GetLayerSet(); - if( d ) - violationLayers &= d->GetLayerSet(); + if( d ) + violationLayers &= d->GetLayerSet(); + } if( violationLayers.count() ) principalLayer = violationLayers.Seq().front(); @@ -339,7 +359,7 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent ) if( ( violationLayers & board->GetVisibleLayers() ) == 0 ) { - m_brdEditor->GetAppearancePanel()->SetLayerVisible( item->GetLayer(), true ); + m_brdEditor->GetAppearancePanel()->SetLayerVisible( principalLayer, true ); m_brdEditor->GetCanvas()->Refresh(); } diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index 83f76a304e..4a5b7346e6 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/pcbnew/dialogs/dialog_export_step.cpp @@ -205,8 +205,8 @@ void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event ) { if( !doAutoSave() ) { - DisplayErrorMessage( this, - _( "STEP export failed! Please save the PCB and try again" ) ); + DisplayErrorMessage( this, _( "STEP export failed! " + "Please save the PCB and try again" ) ); return; } @@ -245,9 +245,9 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent ) wxString msg; // Check if the board outline is continuous - if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, Millimeter2iu( 0.01 ), &msg ) ) + if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, Millimeter2iu( 0.01 ) ) ) { - DisplayErrorMessage( this, _( "Cannot determine the board outline." ), msg ); + DisplayErrorMessage( this, _( "Board outline is malformed. Run DRC for a full analysis." ) ); return; } diff --git a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp index b6838ef6f8..f4ea65924c 100644 --- a/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_courtyard_clearance.cpp @@ -87,18 +87,40 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions() if( !reportProgress( ii++, m_board->Footprints().size(), delta ) ) return; - if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 ) + if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 ) { if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) ) continue; - std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD ); + std::vector discontinuities; + std::vector intersections; - m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(not a closed shape)" ) ); + // Run courtyard test again to get error locations + footprint->BuildPolyCourtyards( &discontinuities, &intersections); - drcItem->SetErrorMessage( m_msg ); - drcItem->SetItems( footprint ); - reportViolation( drcItem, footprint->GetPosition()); + for( wxPoint pt : discontinuities ) + { + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD ); + + m_msg.Printf( _( "(not a closed shape)" ) ); + + drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); + drcItem->SetItems( footprint ); + + reportViolation( drcItem, pt ); + } + + for( wxPoint pt : intersections ) + { + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD ); + + m_msg.Printf( _( "(self-intersecting)" ) ); + + drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); + drcItem->SetItems( footprint ); + + reportViolation( drcItem, pt ); + } } else if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 && footprint->GetPolyCourtyardBack().OutlineCount() == 0 ) @@ -108,7 +130,7 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions() std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD ); drcItem->SetItems( footprint ); - reportViolation( drcItem, footprint->GetPosition()); + reportViolation( drcItem, footprint->GetPosition() ); } else { diff --git a/pcbnew/drc/drc_test_provider_misc.cpp b/pcbnew/drc/drc_test_provider_misc.cpp index 150ac5e546..adb8774c92 100644 --- a/pcbnew/drc/drc_test_provider_misc.cpp +++ b/pcbnew/drc/drc_test_provider_misc.cpp @@ -85,34 +85,43 @@ void DRC_TEST_PROVIDER_MISC::testOutline() SHAPE_POLY_SET boardOutlines; - if( m_board->GetBoardPolygonOutlines( boardOutlines, nullptr, &discontinuities, - &intersections ) ) + if(! m_board->GetBoardPolygonOutlines( boardOutlines, &discontinuities, &intersections ) ) { - return; - } + if( boardOutlines.IsEmpty() ) + { + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE ); - for( wxPoint pt : discontinuities ) - { - std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE ); + m_msg.Printf( _( "(no edges found on Edge.Cuts layer)" ) ); - m_msg.Printf( _( "(not a closed shape)" ) ); + drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); + drcItem->SetItems( m_board ); - drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); - drcItem->SetItems( m_board ); + reportViolation( drcItem, m_board->GetBoundingBox().Centre() ); + } - reportViolation( drcItem, pt ); - } + for( wxPoint pt : discontinuities ) + { + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE ); - for( wxPoint pt : intersections ) - { - std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE ); + m_msg.Printf( _( "(not a closed shape)" ) ); - m_msg.Printf( _( "(self-intersecting)" ) ); + drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); + drcItem->SetItems( m_board ); - drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); - drcItem->SetItems( m_board ); + reportViolation( drcItem, pt ); + } - reportViolation( drcItem, pt ); + for( wxPoint pt : intersections ) + { + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE ); + + m_msg.Printf( _( "(self-intersecting)" ) ); + + drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); + drcItem->SetItems( m_board ); + + reportViolation( drcItem, pt ); + } } } diff --git a/pcbnew/exporters/export_hyperlynx.cpp b/pcbnew/exporters/export_hyperlynx.cpp index 7ff9ff1081..1120774ebd 100644 --- a/pcbnew/exporters/export_hyperlynx.cpp +++ b/pcbnew/exporters/export_hyperlynx.cpp @@ -299,6 +299,7 @@ bool HYPERLYNX_EXPORTER::writeBoardInfo() if( !m_board->GetBoardPolygonOutlines( outlines ) ) { + wxLogError( _( "Board outline is malformed. Run DRC for a full analysis." ) ); return false; } diff --git a/pcbnew/exporters/export_vrml.cpp b/pcbnew/exporters/export_vrml.cpp index ddf0c55986..696cfe36f4 100644 --- a/pcbnew/exporters/export_vrml.cpp +++ b/pcbnew/exporters/export_vrml.cpp @@ -829,13 +829,10 @@ static void export_vrml_drawings( MODEL_VRML& aModel, BOARD* pcb ) static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb ) { SHAPE_POLY_SET pcbOutlines; // stores the board main outlines - wxString msg; - if( !aPcb->GetBoardPolygonOutlines( pcbOutlines, &msg ) ) + if( !aPcb->GetBoardPolygonOutlines( pcbOutlines ) ) { - msg << "\n\n" << - _( "Unable to calculate the board outlines; fall back to using the board boundary box." ); - wxMessageBox( msg ); + wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) ); } int seg; @@ -864,18 +861,14 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb ) if( seg < 0 ) { - msg << "\n\n" << - _( "VRML Export Failed: Could not add holes to contours." ); - wxMessageBox( msg ); - + wxLogError( _( "VRML Export Failed: Could not add holes to contours." ) ); return; } for( int j = 0; j < hole.PointCount(); j++ ) { - aModel.m_holes.AddVertex( seg, (double)hole.CPoint(j).x * BOARD_SCALE, - -((double)hole.CPoint(j).y * BOARD_SCALE ) ); - + aModel.m_holes.AddVertex( seg, (double) hole.CPoint(j).x * BOARD_SCALE, + -( (double) hole.CPoint(j).y * BOARD_SCALE ) ); } aModel.m_holes.EnsureWinding( seg, true ); diff --git a/pcbnew/exporters/gerber_placefile_writer.cpp b/pcbnew/exporters/gerber_placefile_writer.cpp index 9325cb913b..f22e560d7d 100644 --- a/pcbnew/exporters/gerber_placefile_writer.cpp +++ b/pcbnew/exporters/gerber_placefile_writer.cpp @@ -173,16 +173,18 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER // We plot the footprint courtyard when possible. // If not, the pads bounding box will be used. bool useFpPadsBbox = true; + bool onBack = aLayer == B_Cu; footprint->BuildPolyCourtyards(); - if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) == 0 ) + int checkFlag = onBack ? MALFORMED_B_COURTYARD : MALFORMED_F_COURTYARD; + + if( ( footprint->GetFlags() & checkFlag ) == 0 ) { gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_COURTYARD ); - SHAPE_POLY_SET& courtyard = aLayer == B_Cu ? - footprint->GetPolyCourtyardBack(): - footprint->GetPolyCourtyardFront(); + SHAPE_POLY_SET& courtyard = onBack ? footprint->GetPolyCourtyardBack() + : footprint->GetPolyCourtyardFront(); for( int ii = 0; ii < courtyard.OutlineCount(); ii++ ) { diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index dd46ed1238..f6691a8244 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -1693,11 +1693,12 @@ std::shared_ptr FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer ) const } -void FOOTPRINT::BuildPolyCourtyards() +void FOOTPRINT::BuildPolyCourtyards( std::vector* aDiscontinuities, + std::vector* aIntersections ) { m_poly_courtyard_front.RemoveAllContours(); m_poly_courtyard_back.RemoveAllContours(); - ClearFlags( MALFORMED_COURTYARD ); + ClearFlags( MALFORMED_COURTYARDS ); // Build the courtyard area from graphic items on the courtyard. // Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored. @@ -1717,26 +1718,18 @@ void FOOTPRINT::BuildPolyCourtyards() if( !list_front.size() && !list_back.size() ) return; - wxString error_msg; - #define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */ - if( !ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, - (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) ) + + if( !ConvertOutlineToPolygon( list_front, m_poly_courtyard_front, Millimeter2iu( ARC_ERROR_MAX ), + aDiscontinuities, aIntersections ) ) { - SetFlags( MALFORMED_COURTYARD ); + SetFlags( MALFORMED_F_COURTYARD ); } - if( !ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, - (unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) ) + if( !ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, Millimeter2iu( ARC_ERROR_MAX ), + aDiscontinuities, aIntersections ) ) { - SetFlags( MALFORMED_COURTYARD ); - } - - if( !error_msg.IsEmpty() ) - { - wxLogMessage( wxString::Format( _( "Processing courtyard of \"%s\": %s" ), - GetFPID().Format().wx_str(), - error_msg ) ); + SetFlags( MALFORMED_B_COURTYARD ); } } diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h index 674e950feb..ac5e61f8f7 100644 --- a/pcbnew/footprint.h +++ b/pcbnew/footprint.h @@ -666,10 +666,11 @@ public: /** * Builds complex polygons of the courtyard areas from graphic items on the courtyard layers - * @remark sets the MALFORMED_FRONT_COURTYARD and MALFORMED_BACK_COURTYARD status flags if - * the given courtyard layer does not contain a (single) closed shape + * @remark sets the MALFORMED_F_COURTYARD and MALFORMED_B_COURTYARD status flags if the given + * courtyard layer does not contain a (single) closed shape */ - void BuildPolyCourtyards(); + void BuildPolyCourtyards( std::vector* aDiscontinuities = nullptr, + std::vector* aIntersections = nullptr ); virtual std::shared_ptr GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override; diff --git a/pcbnew/pcb_expr_evaluator.cpp b/pcbnew/pcb_expr_evaluator.cpp index f0a6826585..887d0fa743 100644 --- a/pcbnew/pcb_expr_evaluator.cpp +++ b/pcbnew/pcb_expr_evaluator.cpp @@ -250,7 +250,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) { FOOTPRINT* footprint = static_cast( item ); - if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 ) + if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 ) { aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) ); return false; diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index 9c8cdcd36e..1815a4ac49 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -831,10 +831,12 @@ PADSTACK* SPECCTRA_DB::makeVia( const ::VIA* aVia ) void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) { - wxString errMessage; SHAPE_POLY_SET outlines; - aBoard->GetBoardPolygonOutlines( outlines, &errMessage ); + if( !aBoard->GetBoardPolygonOutlines( outlines ) ) + { + wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) ); + } for( int cnt = 0; cnt < outlines.OutlineCount(); cnt++ ) // Should be one outline { @@ -878,9 +880,6 @@ void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) poly_ko->AppendPoint( mapPt( pos ) ); } } - - if( !errMessage.IsEmpty() ) - wxLogMessage( errMessage ); } diff --git a/pcbnew/tools/pcb_inspection_tool.cpp b/pcbnew/tools/pcb_inspection_tool.cpp index 5a19ef71ae..a9c6841c9c 100644 --- a/pcbnew/tools/pcb_inspection_tool.cpp +++ b/pcbnew/tools/pcb_inspection_tool.cpp @@ -39,7 +39,10 @@ void DIALOG_INSPECTION_REPORTER::OnErrorLinkClicked( wxHtmlLinkEvent& event ) { - m_frame->ShowBoardSetupDialog( _( "Rules" ) ); + if( event.GetLinkInfo().GetHref() == "boardsetup" ) + m_frame->ShowBoardSetupDialog( _( "Rules" ) ); + else if( event.GetLinkInfo().GetHref() == "drc" ) + m_frame->GetToolManager()->RunAction( PCB_ACTIONS::runDRC, true ); } @@ -399,6 +402,7 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) BOARD_ITEM* item = static_cast( selection.GetItem( 0 ) ); DRC_ENGINE drcEngine( m_frame->GetBoard(), &m_frame->GetBoard()->GetDesignSettings() ); + bool courtyardError = false; bool compileError = false; try @@ -419,6 +423,9 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) zone->CacheBoundingBox(); footprint->BuildPolyCourtyards(); + + if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 ) + courtyardError = true; } if( item->Type() == PCB_TRACE_T ) @@ -575,6 +582,13 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent ) } else { + if( courtyardError ) + { + r->Report( "" ); + r->Report( _( "Report may be incomplete: some footprint courtyards are malformed. " ) + + "" + _( "Run DRC for a full analysis." ) + "" ); + } + auto constraint = drcEngine.EvalRulesForItems( DISALLOW_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );