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
This commit is contained in:
Jeff Young 2020-11-20 13:55:10 +00:00
parent 0a7ef25453
commit b123318cf6
20 changed files with 173 additions and 146 deletions

View File

@ -461,27 +461,26 @@ bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
{ {
m_board_poly.RemoveAllContours(); m_board_poly.RemoveAllContours();
bool success = false; bool success;
wxString msg;
if( m_board->IsFootprintHolder() ) if( m_board->IsFootprintHolder() )
{ {
success = BuildFootprintPolygonOutlines( m_board, m_board_poly, 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) // Make polygon strictly simple to avoid issues (especially in 3D viewer)
m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
if( aErrorMsg ) if( !success && aErrorMsg )
*aErrorMsg = msg; *aErrorMsg = _( "Footprint outline is malformed. Run DRC for a full analysis." );
} }
else else
{ {
success = m_board->GetBoardPolygonOutlines( m_board_poly, &msg ); success = m_board->GetBoardPolygonOutlines( m_board_poly );
if( aErrorMsg ) if( !success && aErrorMsg )
*aErrorMsg = _( "Board outline is not closed:" ) + wxS( " " )+ msg; *aErrorMsg = _( "Board outline is malformed. Run DRC for a full analysis." );
} }
return success; return success;

View File

@ -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 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_LOCKED (1 << 18) ///< Pcbnew: track locked: protected from global deletion
#define TRACK_AR (1 << 19) ///< Pcbnew: autorouted track #define TRACK_AR (1 << 19) ///< Pcbnew: autorouted track
#define OBSOLETE_1 (1 << 20) ///< Not presently used #define MALFORMED_F_COURTYARD (1 << 20)
#define MALFORMED_COURTYARD (1 << 21) #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 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 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 #define HOLE_PROXY (1 << 24) ///< Indicates the BOARD_ITEM is a proxy for its hole

View File

@ -113,7 +113,7 @@ int AR_AUTOPLACER::genPlacementRoutingMatrix()
return 0; return 0;
// Build the board shape // Build the board shape
m_board->GetBoardPolygonOutlines( m_boardShape /*, aErrorText, aErrorLocation*/ ); m_board->GetBoardPolygonOutlines( m_boardShape );
m_topFreeArea = m_boardShape; m_topFreeArea = m_boardShape;
m_bottomFreeArea = m_boardShape; m_bottomFreeArea = m_boardShape;

View File

@ -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<wxPoint>* aDiscontinuities, std::vector<wxPoint>* aDiscontinuities,
std::vector<wxPoint>* aIntersections ) std::vector<wxPoint>* aIntersections )
{ {
bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError, bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
aErrorText, aDiscontinuities, aIntersections ); aDiscontinuities, aIntersections );
// Make polygon strictly simple to avoid issues (especially in 3D viewer) // Make polygon strictly simple to avoid issues (especially in 3D viewer)
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );

View File

@ -588,8 +588,6 @@ public:
* Any closed outline inside the main outline is a hole * 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 * 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 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 * @param aDiscontinuities = an optional array of wxPoint giving the locations of
* discontinuities in the outline * discontinuities in the outline
* @param aIntersections = an optional array of wxPoint giving the locations of self- * @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 * @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<wxPoint>* aDiscontinuities = nullptr, std::vector<wxPoint>* aDiscontinuities = nullptr,
std::vector<wxPoint>* aIntersections = nullptr ); std::vector<wxPoint>* aIntersections = nullptr );

View File

@ -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 * 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 aSegList the initial list of drawsegments (only lines, circles and arcs).
* @param aPolygons will contain the complex polygon. * @param aPolygons will contain the complex polygon.
* @param aTolerance is the max distance between points that is still accepted as connected * @param aTolerance is the max error distance when polygonizing a curve (internal units)
* (internal units)
* @param aErrorText is a wxString to return error message.
* @param aDiscontinuities = an optional array of wxPoint giving the locations of * @param aDiscontinuities = an optional array of wxPoint giving the locations of
* discontinuities in the outline * discontinuities in the outline
* @param aIntersections = an optional array of wxPoint giving the locations of self- * @param aIntersections = an optional array of wxPoint giving the locations of self-
* intersections in the outline * intersections in the outline
*/ */
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons, bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
unsigned int aTolerance, wxString* aErrorText, int aTolerance, std::vector<wxPoint>* aDiscontinuities,
std::vector<wxPoint>* aDiscontinuities,
std::vector<wxPoint>* aIntersections ) std::vector<wxPoint>* aIntersections )
{ {
if( aSegList.size() == 0 ) if( aSegList.size() == 0 )
@ -519,15 +516,6 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
} }
else // encountered discontinuity 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 ) if( aDiscontinuities )
aDiscontinuities->emplace_back( prevPt ); aDiscontinuities->emplace_back( prevPt );
@ -731,15 +719,6 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
} }
else // encountered discontinuity 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 ) if( aDiscontinuities )
aDiscontinuities->emplace_back( prevPt ); aDiscontinuities->emplace_back( prevPt );
@ -795,8 +774,8 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
* Any closed outline inside the main outline is a hole * Any closed outline inside the main outline is a hole
* All contours should be closed, i.e. valid closed polygon vertices * All contours should be closed, i.e. valid closed polygon vertices
*/ */
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, unsigned int aTolerance, bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance,
wxString* aErrorText, std::vector<wxPoint>* aDiscontinuities, std::vector<wxPoint>* aDiscontinuities,
std::vector<wxPoint>* aIntersections ) std::vector<wxPoint>* aIntersections )
{ {
PCB_TYPE_COLLECTOR items; PCB_TYPE_COLLECTOR items;
@ -817,12 +796,8 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, unsign
if( segList.size() ) if( segList.size() )
{ {
success = ConvertOutlineToPolygon( segList, aOutlines, aTolerance, aErrorText, success = ConvertOutlineToPolygon( segList, aOutlines, aTolerance, aDiscontinuities,
aDiscontinuities, aIntersections ); aIntersections );
}
else if( aErrorText )
{
*aErrorText = _( "No edges found on Edge.Cuts layer." );
} }
if( !success || !aOutlines.OutlineCount() ) if( !success || !aOutlines.OutlineCount() )
@ -1035,8 +1010,8 @@ bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) ); segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
} }
bool success = ConvertOutlineToPolygon( segList, outlines, aTolerance, aErrorText, bool success = ConvertOutlineToPolygon( segList, outlines, aTolerance, aDiscontinuities,
aDiscontinuities, aIntersections ); aIntersections );
FOOTPRINT* footprint = aBoard->GetFirstFootprint(); FOOTPRINT* footprint = aBoard->GetFirstFootprint();

View File

@ -41,17 +41,14 @@ class wxPoint;
* These closed inner outlines are considered as holes in the main outline * 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 aSegList the initial list of drawsegments (only lines, circles and arcs).
* @param aPolygons will contain the complex polygon. * @param aPolygons will contain the complex polygon.
* @param aTolerance is the max distance between points that is still accepted as connected * @param aTolerance is the max error distance when polygonizing a curve (internal units)
* (internal units)
* @param aErrorText is a wxString to return error message.
* @param aDiscontinuities = an optional array of wxPoint giving the locations of * @param aDiscontinuities = an optional array of wxPoint giving the locations of
* discontinuities in the outline * discontinuities in the outline
* @param aIntersections = an optional array of wxPoint giving the locations of self- * @param aIntersections = an optional array of wxPoint giving the locations of self-
* intersections in the outline * intersections in the outline
*/ */
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons, bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
unsigned int aTolerance, wxString* aErrorText, int aTolerance, std::vector<wxPoint>* aDiscontinuities = nullptr,
std::vector<wxPoint>* aDiscontinuities = nullptr,
std::vector<wxPoint>* aIntersections = nullptr ); std::vector<wxPoint>* aIntersections = nullptr );
@ -62,8 +59,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
* All contours should be closed, i.e. are valid vertices for a closed polygon * 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 * return true if success, false if a contour is not valid
*/ */
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aTolerance,
unsigned int aTolerance, wxString* aErrorText,
std::vector<wxPoint>* aDiscontinuities = nullptr, std::vector<wxPoint>* aDiscontinuities = nullptr,
std::vector<wxPoint>* aIntersections = nullptr ); std::vector<wxPoint>* aIntersections = nullptr );

View File

@ -23,8 +23,9 @@
#include <dialog_constraints_reporter.h> #include <dialog_constraints_reporter.h>
#include <pcb_edit_frame.h> #include <pcb_edit_frame.h>
#include <tool/tool_manager.h>
#include <wx_html_report_box.h> #include <wx_html_report_box.h>
#include <tools/pcb_actions.h>
DIALOG_CONSTRAINTS_REPORTER::DIALOG_CONSTRAINTS_REPORTER( PCB_EDIT_FRAME* aParent ) : DIALOG_CONSTRAINTS_REPORTER::DIALOG_CONSTRAINTS_REPORTER( PCB_EDIT_FRAME* aParent ) :
DIALOG_CONSTRAINTS_REPORTER_BASE( aParent ), DIALOG_CONSTRAINTS_REPORTER_BASE( aParent ),
@ -47,7 +48,10 @@ void DIALOG_CONSTRAINTS_REPORTER::DeleteAllPages()
void DIALOG_CONSTRAINTS_REPORTER::OnErrorLinkClicked( wxHtmlLinkEvent& event ) 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 );
} }

View File

@ -306,26 +306,46 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
if( item && node ) if( item && node )
{ {
PCB_LAYER_ID principalLayer = item->GetLayer(); PCB_LAYER_ID principalLayer = item->GetLayer();
std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem; LSET violationLayers;
std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() ); if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() ); {
BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() ); BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() );
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 ) if( a )
violationLayers &= a->GetLayerSet(); violationLayers &= a->GetLayerSet();
if( b ) if( b )
violationLayers &= b->GetLayerSet(); violationLayers &= b->GetLayerSet();
if( c ) if( c )
violationLayers &= c->GetLayerSet(); violationLayers &= c->GetLayerSet();
if( d ) if( d )
violationLayers &= d->GetLayerSet(); violationLayers &= d->GetLayerSet();
}
if( violationLayers.count() ) if( violationLayers.count() )
principalLayer = violationLayers.Seq().front(); principalLayer = violationLayers.Seq().front();
@ -339,7 +359,7 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
if( ( violationLayers & board->GetVisibleLayers() ) == 0 ) if( ( violationLayers & board->GetVisibleLayers() ) == 0 )
{ {
m_brdEditor->GetAppearancePanel()->SetLayerVisible( item->GetLayer(), true ); m_brdEditor->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
m_brdEditor->GetCanvas()->Refresh(); m_brdEditor->GetCanvas()->Refresh();
} }

View File

@ -205,8 +205,8 @@ void PCB_EDIT_FRAME::OnExportSTEP( wxCommandEvent& event )
{ {
if( !doAutoSave() ) if( !doAutoSave() )
{ {
DisplayErrorMessage( this, DisplayErrorMessage( this, _( "STEP export failed! "
_( "STEP export failed! Please save the PCB and try again" ) ); "Please save the PCB and try again" ) );
return; return;
} }
@ -245,9 +245,9 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
wxString msg; wxString msg;
// Check if the board outline is continuous // 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; return;
} }

View File

@ -87,18 +87,40 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
if( !reportProgress( ii++, m_board->Footprints().size(), delta ) ) if( !reportProgress( ii++, m_board->Footprints().size(), delta ) )
return; return;
if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 ) if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
{ {
if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) ) if( m_drcEngine->IsErrorLimitExceeded( DRCE_MALFORMED_COURTYARD) )
continue; continue;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD ); std::vector<wxPoint> discontinuities;
std::vector<wxPoint> 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 ); for( wxPoint pt : discontinuities )
drcItem->SetItems( footprint ); {
reportViolation( drcItem, footprint->GetPosition()); std::shared_ptr<DRC_ITEM> 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<DRC_ITEM> 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 else if( footprint->GetPolyCourtyardFront().OutlineCount() == 0
&& footprint->GetPolyCourtyardBack().OutlineCount() == 0 ) && footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
@ -108,7 +130,7 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testFootprintCourtyardDefinitions()
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD ); std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MISSING_COURTYARD );
drcItem->SetItems( footprint ); drcItem->SetItems( footprint );
reportViolation( drcItem, footprint->GetPosition()); reportViolation( drcItem, footprint->GetPosition() );
} }
else else
{ {

View File

@ -85,34 +85,43 @@ void DRC_TEST_PROVIDER_MISC::testOutline()
SHAPE_POLY_SET boardOutlines; SHAPE_POLY_SET boardOutlines;
if( m_board->GetBoardPolygonOutlines( boardOutlines, nullptr, &discontinuities, if(! m_board->GetBoardPolygonOutlines( boardOutlines, &discontinuities, &intersections ) )
&intersections ) )
{ {
return; if( boardOutlines.IsEmpty() )
} {
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
for( wxPoint pt : discontinuities ) m_msg.Printf( _( "(no edges found on Edge.Cuts layer)" ) );
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
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 ); reportViolation( drcItem, m_board->GetBoundingBox().Centre() );
drcItem->SetItems( m_board ); }
reportViolation( drcItem, pt ); for( wxPoint pt : discontinuities )
} {
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
for( wxPoint pt : intersections ) m_msg.Printf( _( "(not a closed shape)" ) );
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
m_msg.Printf( _( "(self-intersecting)" ) ); drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg );
drcItem->SetItems( m_board );
drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + m_msg ); reportViolation( drcItem, pt );
drcItem->SetItems( m_board ); }
reportViolation( drcItem, pt ); for( wxPoint pt : intersections )
{
std::shared_ptr<DRC_ITEM> 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 );
}
} }
} }

View File

@ -299,6 +299,7 @@ bool HYPERLYNX_EXPORTER::writeBoardInfo()
if( !m_board->GetBoardPolygonOutlines( outlines ) ) if( !m_board->GetBoardPolygonOutlines( outlines ) )
{ {
wxLogError( _( "Board outline is malformed. Run DRC for a full analysis." ) );
return false; return false;
} }

View File

@ -829,13 +829,10 @@ static void export_vrml_drawings( MODEL_VRML& aModel, BOARD* pcb )
static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb ) static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb )
{ {
SHAPE_POLY_SET pcbOutlines; // stores the board main outlines SHAPE_POLY_SET pcbOutlines; // stores the board main outlines
wxString msg;
if( !aPcb->GetBoardPolygonOutlines( pcbOutlines, &msg ) ) if( !aPcb->GetBoardPolygonOutlines( pcbOutlines ) )
{ {
msg << "\n\n" << wxLogWarning( _( "Board outline is malformed. Run DRC for a full analysis." ) );
_( "Unable to calculate the board outlines; fall back to using the board boundary box." );
wxMessageBox( msg );
} }
int seg; int seg;
@ -864,18 +861,14 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* aPcb )
if( seg < 0 ) if( seg < 0 )
{ {
msg << "\n\n" << wxLogError( _( "VRML Export Failed: Could not add holes to contours." ) );
_( "VRML Export Failed: Could not add holes to contours." );
wxMessageBox( msg );
return; return;
} }
for( int j = 0; j < hole.PointCount(); j++ ) for( int j = 0; j < hole.PointCount(); j++ )
{ {
aModel.m_holes.AddVertex( seg, (double)hole.CPoint(j).x * BOARD_SCALE, aModel.m_holes.AddVertex( seg, (double) hole.CPoint(j).x * BOARD_SCALE,
-((double)hole.CPoint(j).y * BOARD_SCALE ) ); -( (double) hole.CPoint(j).y * BOARD_SCALE ) );
} }
aModel.m_holes.EnsureWinding( seg, true ); aModel.m_holes.EnsureWinding( seg, true );

View File

@ -173,16 +173,18 @@ int PLACEFILE_GERBER_WRITER::CreatePlaceFile( wxString& aFullFilename, PCB_LAYER
// We plot the footprint courtyard when possible. // We plot the footprint courtyard when possible.
// If not, the pads bounding box will be used. // If not, the pads bounding box will be used.
bool useFpPadsBbox = true; bool useFpPadsBbox = true;
bool onBack = aLayer == B_Cu;
footprint->BuildPolyCourtyards(); 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 ); gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CMP_COURTYARD );
SHAPE_POLY_SET& courtyard = aLayer == B_Cu ? SHAPE_POLY_SET& courtyard = onBack ? footprint->GetPolyCourtyardBack()
footprint->GetPolyCourtyardBack(): : footprint->GetPolyCourtyardFront();
footprint->GetPolyCourtyardFront();
for( int ii = 0; ii < courtyard.OutlineCount(); ii++ ) for( int ii = 0; ii < courtyard.OutlineCount(); ii++ )
{ {

View File

@ -1693,11 +1693,12 @@ std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
} }
void FOOTPRINT::BuildPolyCourtyards() void FOOTPRINT::BuildPolyCourtyards( std::vector<wxPoint>* aDiscontinuities,
std::vector<wxPoint>* aIntersections )
{ {
m_poly_courtyard_front.RemoveAllContours(); m_poly_courtyard_front.RemoveAllContours();
m_poly_courtyard_back.RemoveAllContours(); m_poly_courtyard_back.RemoveAllContours();
ClearFlags( MALFORMED_COURTYARD ); ClearFlags( MALFORMED_COURTYARDS );
// Build the courtyard area from graphic items on the courtyard. // Build the courtyard area from graphic items on the courtyard.
// Only PCB_FP_SHAPE_T have meaning, graphic texts are ignored. // 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() ) if( !list_front.size() && !list_back.size() )
return; return;
wxString error_msg;
#define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */ #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, if( !ConvertOutlineToPolygon( list_back, m_poly_courtyard_back, Millimeter2iu( ARC_ERROR_MAX ),
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg ) ) aDiscontinuities, aIntersections ) )
{ {
SetFlags( MALFORMED_COURTYARD ); SetFlags( MALFORMED_B_COURTYARD );
}
if( !error_msg.IsEmpty() )
{
wxLogMessage( wxString::Format( _( "Processing courtyard of \"%s\": %s" ),
GetFPID().Format().wx_str(),
error_msg ) );
} }
} }

View File

@ -666,10 +666,11 @@ public:
/** /**
* Builds complex polygons of the courtyard areas from graphic items on the courtyard layers * 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 * @remark sets the MALFORMED_F_COURTYARD and MALFORMED_B_COURTYARD status flags if the given
* the given courtyard layer does not contain a (single) closed shape * courtyard layer does not contain a (single) closed shape
*/ */
void BuildPolyCourtyards(); void BuildPolyCourtyards( std::vector<wxPoint>* aDiscontinuities = nullptr,
std::vector<wxPoint>* aIntersections = nullptr );
virtual std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override; virtual std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer = UNDEFINED_LAYER ) const override;

View File

@ -250,7 +250,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
{ {
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item ); FOOTPRINT* footprint = static_cast<FOOTPRINT*>( item );
if( ( footprint->GetFlags() & MALFORMED_COURTYARD ) != 0 ) if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
{ {
aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) ); aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
return false; return false;

View File

@ -831,10 +831,12 @@ PADSTACK* SPECCTRA_DB::makeVia( const ::VIA* aVia )
void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary )
{ {
wxString errMessage;
SHAPE_POLY_SET outlines; 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 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 ) ); poly_ko->AppendPoint( mapPt( pos ) );
} }
} }
if( !errMessage.IsEmpty() )
wxLogMessage( errMessage );
} }

View File

@ -39,7 +39,10 @@
void DIALOG_INSPECTION_REPORTER::OnErrorLinkClicked( wxHtmlLinkEvent& event ) 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<BOARD_ITEM*>( selection.GetItem( 0 ) ); BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
DRC_ENGINE drcEngine( m_frame->GetBoard(), &m_frame->GetBoard()->GetDesignSettings() ); DRC_ENGINE drcEngine( m_frame->GetBoard(), &m_frame->GetBoard()->GetDesignSettings() );
bool courtyardError = false;
bool compileError = false; bool compileError = false;
try try
@ -419,6 +423,9 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
zone->CacheBoundingBox(); zone->CacheBoundingBox();
footprint->BuildPolyCourtyards(); footprint->BuildPolyCourtyards();
if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
courtyardError = true;
} }
if( item->Type() == PCB_TRACE_T ) if( item->Type() == PCB_TRACE_T )
@ -575,6 +582,13 @@ int PCB_INSPECTION_TOOL::InspectConstraints( const TOOL_EVENT& aEvent )
} }
else else
{ {
if( courtyardError )
{
r->Report( "" );
r->Report( _( "Report may be incomplete: some footprint courtyards are malformed. " )
+ "<a href='drc'>" + _( "Run DRC for a full analysis." ) + "</a>" );
}
auto constraint = drcEngine.EvalRulesForItems( DISALLOW_CONSTRAINT, item, nullptr, auto constraint = drcEngine.EvalRulesForItems( DISALLOW_CONSTRAINT, item, nullptr,
UNDEFINED_LAYER, r ); UNDEFINED_LAYER, r );