DRC_TEST_PROVIDER_MISC::testOutline(): add test to detect questionable items.

Graphic items (segments, rects, circles) on Edge.Cuts can create issues when
building board outlines, when they are very small (a few nm in size), because
they are not easily handled when trying to search connected graphics.
Also protect RC_ITEM::SetItems() against null pointer.
Fixes #15865
https://gitlab.com/kicad/code/kicad/-/issues/15865
This commit is contained in:
jean-pierre charras 2023-10-12 14:03:58 +02:00
parent b0f4adf052
commit 499f3ca95b
4 changed files with 146 additions and 4 deletions

View File

@ -64,6 +64,7 @@ void RC_ITEM::SetItems( const EDA_ITEM* aItem, const EDA_ITEM* bItem,
{
m_ids.clear();
if( aItem )
m_ids.push_back( aItem->m_Uuid );
if( bItem )

View File

@ -35,6 +35,8 @@
#include <geometry/shape_poly_set.h>
#include <geometry/geometry_utils.h>
#include <convert_shape_list_to_polygon.h>
#include <board.h>
#include <collectors.h>
#include <wx/log.h>
@ -582,8 +584,116 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SE
}
#include <board.h>
#include <collectors.h>
/* This function is used to test a board outlines graphic items for validity
* i.e. null or very small segments, rects and circles
*/
bool TestBoardOutlinesGraphicItems( BOARD* aBoard, int aMinDist,
OUTLINE_ERROR_HANDLER* aErrorHandler )
{
bool success = true;
PCB_TYPE_COLLECTOR items;
int min_dist = std::max( 0, aMinDist );
// Get all the shapes into 'items', then keep only those on layer == Edge_Cuts.
items.Collect( aBoard, { PCB_SHAPE_T } );
std::vector<PCB_SHAPE*> segList;
for( int ii = 0; ii < items.GetCount(); ii++ )
{
PCB_SHAPE* seg = static_cast<PCB_SHAPE*>( items[ii] );
if( seg->GetLayer() == Edge_Cuts )
segList.push_back( seg );
}
for( FOOTPRINT* fp : aBoard->Footprints() )
{
PCB_TYPE_COLLECTOR fpItems;
fpItems.Collect( fp, { PCB_SHAPE_T } );
for( int ii = 0; ii < fpItems.GetCount(); ii++ )
{
PCB_SHAPE* fpSeg = static_cast<PCB_SHAPE*>( fpItems[ii] );
if( fpSeg->GetLayer() == Edge_Cuts )
segList.push_back( fpSeg );
}
}
// Now Test vailidty of collected items
for( PCB_SHAPE* graphic : segList )
{
switch( graphic->GetShape() )
{
case SHAPE_T::RECTANGLE:
{
VECTOR2I seg = graphic->GetEnd() - graphic->GetStart();
int dim = seg.EuclideanNorm();
if( dim <= min_dist )
{
success = false;
if( aErrorHandler )
{
(*aErrorHandler)( wxString::Format( _( "(Rectangle has null or very small size: %d nm)" ),
dim ),
graphic, nullptr, graphic->GetStart() );
}
}
break;
}
case SHAPE_T::CIRCLE:
{
if( graphic->GetRadius() <= min_dist )
{
success = false;
if( aErrorHandler )
{
(*aErrorHandler)( wxString::Format( _( "(Circle has null or very small radius: %d nm)" ),
(int)graphic->GetRadius() ),
graphic, nullptr, graphic->GetStart() );
}
}
break;
}
case SHAPE_T::SEGMENT:
{
VECTOR2I seg = graphic->GetEnd() - graphic->GetStart();
int dim = seg.EuclideanNorm();
if( dim <= min_dist )
{
success = false;
if( aErrorHandler )
{
(*aErrorHandler)( wxString::Format( _( "(Segment has null or very small lenght: %d nm)" ), dim ),
graphic, nullptr, graphic->GetStart() );
}
}
break;
}
case SHAPE_T::ARC:
break;
case SHAPE_T::BEZIER:
break;
default:
UNIMPLEMENTED_FOR( graphic->SHAPE_T_asString() );
return false;
}
}
return success;
}
/* This function is used to extract a board outlines (3D view, automatic zones build ...)
* Any closed outline inside the main outline is a hole

View File

@ -32,6 +32,16 @@ typedef
const std::function<void( const wxString& msg, BOARD_ITEM* itemA, BOARD_ITEM* itemB,
const VECTOR2I& pt )> OUTLINE_ERROR_HANDLER;
/**
* This function is used to test a board graphic items on Edge cut layer for validity
* i.e. null segments, 0 size rects and circles
* @param aBoard is the board to test
* @param aMinDist is the min lenght of a segment (or radius, or diagonal size of a rect)
* to be valid
* @param aErrorHandler = an optional error handler
*/
bool TestBoardOutlinesGraphicItems( BOARD* aBoard, int aMinDist,
OUTLINE_ERROR_HANDLER* aErrorHandler );
/**
* Function ConvertOutlineToPolygon
* build a polygon set (with holes) from a PCB_SHAPE list, which is expected to be one or more

View File

@ -98,9 +98,30 @@ void DRC_TEST_PROVIDER_MISC::testOutline()
};
// Use the standard chaining epsilon here so that we report errors that might affect
// other tools (such as STEP export).
// other tools (such as 3D viewer).
int chainingEpsilon = m_board->GetOutlinesChainingEpsilon();
if( !TestBoardOutlinesGraphicItems(m_board, chainingEpsilon, &errorHandler ) )
{
if( errorHandled )
{
// if there are invalid items on Edge.Cuts, they are already reported
}
else
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
wxString msg;
msg.Printf( _( "(Suspicious items found on Edge.Cuts layer)" ) );
drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + msg );
drcItem->SetItems( m_board );
reportViolation( drcItem, m_board->GetBoundingBox().Centre(), Edge_Cuts );
}
}
if( !BuildBoardPolygonOutlines( m_board, dummyOutline, m_board->GetDesignSettings().m_MaxError,
chainingEpsilon, &errorHandler ) )
{