From 499f3ca95b48e324f7d5ffa0e1df5bf02166c3f9 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Thu, 12 Oct 2023 14:03:58 +0200 Subject: [PATCH] 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 --- common/rc_item.cpp | 3 +- pcbnew/convert_shape_list_to_polygon.cpp | 114 ++++++++++++++++++++++- pcbnew/convert_shape_list_to_polygon.h | 10 ++ pcbnew/drc/drc_test_provider_misc.cpp | 23 ++++- 4 files changed, 146 insertions(+), 4 deletions(-) diff --git a/common/rc_item.cpp b/common/rc_item.cpp index 0157428aaf..fb0abade0f 100644 --- a/common/rc_item.cpp +++ b/common/rc_item.cpp @@ -64,7 +64,8 @@ void RC_ITEM::SetItems( const EDA_ITEM* aItem, const EDA_ITEM* bItem, { m_ids.clear(); - m_ids.push_back( aItem->m_Uuid ); + if( aItem ) + m_ids.push_back( aItem->m_Uuid ); if( bItem ) m_ids.push_back( bItem->m_Uuid ); diff --git a/pcbnew/convert_shape_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp index b21914b55f..8bc4d74309 100644 --- a/pcbnew/convert_shape_list_to_polygon.cpp +++ b/pcbnew/convert_shape_list_to_polygon.cpp @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include @@ -582,8 +584,116 @@ bool ConvertOutlineToPolygon( std::vector& aShapeList, SHAPE_POLY_SE } -#include -#include +/* 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 segList; + + for( int ii = 0; ii < items.GetCount(); ii++ ) + { + PCB_SHAPE* seg = static_cast( 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( 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 diff --git a/pcbnew/convert_shape_list_to_polygon.h b/pcbnew/convert_shape_list_to_polygon.h index cb938c5beb..acc512abd0 100644 --- a/pcbnew/convert_shape_list_to_polygon.h +++ b/pcbnew/convert_shape_list_to_polygon.h @@ -32,6 +32,16 @@ typedef const std::function 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 diff --git a/pcbnew/drc/drc_test_provider_misc.cpp b/pcbnew/drc/drc_test_provider_misc.cpp index a530e1b941..260cf53bb2 100644 --- a/pcbnew/drc/drc_test_provider_misc.cpp +++ b/pcbnew/drc/drc_test_provider_misc.cpp @@ -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 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 ) ) {