From fc1afa6298a4f72f660107ac069e535d99f6633d Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Wed, 27 Jul 2022 17:15:36 +0100 Subject: [PATCH] Improvements to convert tool. 1) Handle filled shapes 2) If a chained outline can't be found, build hulls of the individual segments 3) Combine intersecting shapes 4) Simplify code Fixes https://gitlab.com/kicad/code/kicad/issues/git Fixes https://gitlab.com/kicad/code/kicad/issues/5911 --- pcbnew/tools/convert_tool.cpp | 176 +++++++++------------------------- pcbnew/tools/convert_tool.h | 24 +---- 2 files changed, 51 insertions(+), 149 deletions(-) diff --git a/pcbnew/tools/convert_tool.cpp b/pcbnew/tools/convert_tool.cpp index 1e1f3f26d9..234b432d36 100644 --- a/pcbnew/tools/convert_tool.cpp +++ b/pcbnew/tools/convert_tool.cpp @@ -123,71 +123,29 @@ bool CONVERT_TOOL::Init() int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent ) { - FOOTPRINT* parentFootprint = nullptr; + FOOTPRINT* parentFootprint = nullptr; - auto& selection = m_selectionTool->RequestSelection( + PCB_SELECTION& selection = m_selectionTool->RequestSelection( []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool ) { - for( int i = aCollector.GetCount() - 1; i >= 0; --i ) - { - BOARD_ITEM* item = aCollector[i]; - - switch( item->Type() ) - { - case PCB_SHAPE_T: - case PCB_FP_SHAPE_T: - { - PCB_SHAPE* shape = static_cast( item ); - - switch( shape->GetShape() ) - { - case SHAPE_T::SEGMENT: - case SHAPE_T::RECT: - case SHAPE_T::CIRCLE: - case SHAPE_T::ARC: - if( shape->GetStart() == shape->GetEnd() ) - aCollector.Remove( item ); - - break; - - case SHAPE_T::POLY: - break; - - default: - aCollector.Remove( item ); - } - - break; - } - case PCB_TRACE_T: - case PCB_ARC_T: - break; - - case PCB_ZONE_T: - case PCB_FP_ZONE_T: - break; - - default: - aCollector.Remove( item ); - } - } } ); if( selection.Empty() ) return 0; PCB_LAYER_ID destLayer = m_frame->GetActiveLayer(); - SHAPE_POLY_SET polySet = makePolysFromSegs( selection.GetItems() ); + SHAPE_POLY_SET polySet; - polySet.Append( makePolysFromRects( selection.GetItems() ) ); + if( !IsCopperLayer( destLayer ) ) + polySet = makePolysFromChainedSegs( selection.GetItems() ); - polySet.Append( makePolysFromCircles( selection.GetItems() ) ); - - polySet.Append( extractPolygons( selection.GetItems() ) ); + polySet.Append( makePolysFromGraphics( selection.GetItems() ) ); if( polySet.IsEmpty() ) return 0; + polySet.Simplify( SHAPE_POLY_SET::PM_FAST ); + bool isFootprint = m_frame->IsType( FRAME_FOOTPRINT_EDITOR ); if( isFootprint ) @@ -205,8 +163,13 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent ) // For now, we convert each outline in the returned shape to its own polygon std::vector polys; - for( int i = 0; i < polySet.OutlineCount(); i++ ) - polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( i ) ) ); + for( int ii = 0; ii < polySet.OutlineCount(); ++ii ) + { + polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( ii ) ) ); + + for( int jj = 0; jj < polySet.HoleCount( ii ); ++jj ) + polys.back().AddHole( polySet.Hole( ii, jj ) ); + } if( aEvent.IsAction( &PCB_ACTIONS::convertToPoly ) ) { @@ -276,7 +239,7 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent ) } -SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aItems ) +SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque& aItems ) { // TODO: This code has a somewhat-similar purpose to ConvertOutlineToPolygon but is slightly // different, so this remains a separate algorithm. It might be nice to analyze the dfiferences @@ -289,7 +252,6 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B std::map>> connections; - std::set used; std::deque toCheck; auto closeEnough = @@ -315,6 +277,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt for( EDA_ITEM* item : aItems ) { + item->ClearFlags( SKIP_STRUCT ); + if( OPT seg = getStartEndPoints( item, nullptr ) ) { toCheck.push_back( item ); @@ -328,7 +292,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt EDA_ITEM* candidate = toCheck.front(); toCheck.pop_front(); - if( used.count( candidate ) ) + if( candidate->GetFlags() & SKIP_STRUCT ) continue; int width = -1; @@ -379,10 +343,10 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt std::function process = [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection ) { - if( used.count( aItem ) ) + if( aItem->GetFlags() & SKIP_STRUCT ) return; - used.insert( aItem ); + aItem->SetFlags( SKIP_STRUCT ); insert( aItem, aAnchor, aDirection ); @@ -446,87 +410,27 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt } -SHAPE_POLY_SET CONVERT_TOOL::makePolysFromRects( const std::deque& aItems ) +SHAPE_POLY_SET CONVERT_TOOL::makePolysFromGraphics( const std::deque& aItems ) { - SHAPE_POLY_SET poly; + BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings(); + SHAPE_POLY_SET poly; for( EDA_ITEM* item : aItems ) { - if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T ) + if( item->GetFlags() & SKIP_STRUCT ) continue; - PCB_SHAPE* graphic = static_cast( item ); - - if( graphic->GetShape() != SHAPE_T::RECT ) - continue; - - SHAPE_LINE_CHAIN outline; - VECTOR2I start( graphic->GetStart() ); - VECTOR2I end( graphic->GetEnd() ); - - outline.Append( start ); - outline.Append( VECTOR2I( end.x, start.y ) ); - outline.Append( end ); - outline.Append( VECTOR2I( start.x, end.y ) ); - outline.SetClosed( true ); - - outline.SetWidth( graphic->GetWidth() ); - - poly.AddOutline( outline ); - } - - return poly; -} - - -SHAPE_POLY_SET CONVERT_TOOL::makePolysFromCircles( const std::deque& aItems ) -{ - SHAPE_POLY_SET poly; - - for( EDA_ITEM* item : aItems ) - { - if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T ) - continue; - - PCB_SHAPE* graphic = static_cast( item ); - - if( graphic->GetShape() != SHAPE_T::CIRCLE ) - continue; - - BOARD_DESIGN_SETTINGS& bds = graphic->GetBoard()->GetDesignSettings(); - SHAPE_LINE_CHAIN outline; - - TransformCircleToPolygon( outline, graphic->GetPosition(), graphic->GetRadius(), - bds.m_MaxError, ERROR_OUTSIDE ); - - poly.AddOutline( outline ); - } - - return poly; -} - - -SHAPE_POLY_SET CONVERT_TOOL::extractPolygons( const std::deque& aItems ) -{ - SHAPE_POLY_SET poly; - - for( EDA_ITEM* item : aItems ) - { switch( item->Type() ) { case PCB_SHAPE_T: case PCB_FP_SHAPE_T: - switch( static_cast( item )->GetShape() ) - { - case SHAPE_T::POLY: - poly.Append( static_cast( item )->GetPolyShape() ); - break; - - default: - continue; - } + { + PCB_SHAPE* graphic = static_cast( item ); + graphic->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0, + bds.m_MaxError, ERROR_INSIDE ); break; + } case PCB_ZONE_T: case PCB_FP_ZONE_T: @@ -881,11 +785,23 @@ OPT CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth ) { PCB_SHAPE* shape = static_cast( aItem ); - if( aWidth ) - *aWidth = shape->GetWidth(); + switch( shape->GetShape() ) + { + case SHAPE_T::SEGMENT: + case SHAPE_T::ARC: + case SHAPE_T::POLY: + if( shape->GetStart() == shape->GetEnd() ) + return NULLOPT; - return boost::make_optional( { VECTOR2I( shape->GetStart() ), - VECTOR2I( shape->GetEnd() ) } ); + if( aWidth ) + *aWidth = shape->GetWidth(); + + return boost::make_optional( { VECTOR2I( shape->GetStart() ), + VECTOR2I( shape->GetEnd() ) } ); + + default: + return NULLOPT; + } } case PCB_TRACE_T: diff --git a/pcbnew/tools/convert_tool.h b/pcbnew/tools/convert_tool.h index e0629a9c8d..0b19896cf2 100644 --- a/pcbnew/tools/convert_tool.h +++ b/pcbnew/tools/convert_tool.h @@ -73,7 +73,7 @@ private: static OPT getStartEndPoints( EDA_ITEM* aItem, int* aWidth ); /** - * Try to make polygons from segments in the selected items. + * Try to make polygons from chained segments in the selected items. * * Polygons are formed from chains of lines/arcs. Each set containing two or more lines/arcs * that are connected will be added to the return SHAPE_POLY_SET as an outline. No attempt @@ -82,29 +82,15 @@ private: * @param aItems is a list of items to process. * @return a #SHAPE_POLY_SET containing any polygons that were created. */ - static SHAPE_POLY_SET makePolysFromSegs( const std::deque& aItems ); + SHAPE_POLY_SET makePolysFromChainedSegs( const std::deque& aItems ); /** - * Try to make polygons from rectangles. + * Make polygons from graphic shapes and zones. * - * @param aItems is a list of rect shapes to process. + * @param aItems is a list of items to process. * @return a #SHAPE_POLY_SET containing any polygons that were created. */ - static SHAPE_POLY_SET makePolysFromRects( const std::deque& aItems ); - - /** - * Try to make polygons from circles. - * - * @param aItems is a list of circle shapes to process. - * @return a #SHAPE_POLY_SET containing any polygons that were created. - */ - static SHAPE_POLY_SET makePolysFromCircles( const std::deque& aItems ); - - /** - * For any polygon shapes (zones, keepouts, graphic polys) in aItems, extracts - * the polygon outlines into the returned #SHAPE_POLY_SET. - */ - static SHAPE_POLY_SET extractPolygons( const std::deque& aItems ); + SHAPE_POLY_SET makePolysFromGraphics( const std::deque& aItems ); PCB_SELECTION_TOOL* m_selectionTool; CONDITIONAL_MENU* m_menu;