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
This commit is contained in:
Jeff Young 2022-07-27 17:15:36 +01:00
parent bb43d923af
commit fc1afa6298
2 changed files with 51 additions and 149 deletions

View File

@ -125,69 +125,27 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
{
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<PCB_SHAPE*>( 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<SHAPE_POLY_SET> 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<EDA_ITEM*>& aItems )
SHAPE_POLY_SET CONVERT_TOOL::makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& 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<EDA_ITEM*>& aIt
// Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B
std::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
std::set<EDA_ITEM*> used;
std::deque<EDA_ITEM*> toCheck;
auto closeEnough =
@ -315,6 +277,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
for( EDA_ITEM* item : aItems )
{
item->ClearFlags( SKIP_STRUCT );
if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
{
toCheck.push_back( item );
@ -328,7 +292,7 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& 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<EDA_ITEM*>& aIt
std::function<void( EDA_ITEM*, VECTOR2I, bool )> 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,88 +410,28 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
}
SHAPE_POLY_SET CONVERT_TOOL::makePolysFromRects( const std::deque<EDA_ITEM*>& aItems )
SHAPE_POLY_SET CONVERT_TOOL::makePolysFromGraphics( const std::deque<EDA_ITEM*>& aItems )
{
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<PCB_SHAPE*>( 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<EDA_ITEM*>& 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<PCB_SHAPE*>( 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<EDA_ITEM*>& 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<PCB_SHAPE*>( item )->GetShape() )
{
case SHAPE_T::POLY:
poly.Append( static_cast<PCB_SHAPE*>( item )->GetPolyShape() );
break;
PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
default:
continue;
graphic->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0,
bds.m_MaxError, ERROR_INSIDE );
break;
}
break;
case PCB_ZONE_T:
case PCB_FP_ZONE_T:
poly.Append( *static_cast<ZONE*>( item )->Outline() );
@ -881,11 +785,23 @@ OPT<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth )
{
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
switch( shape->GetShape() )
{
case SHAPE_T::SEGMENT:
case SHAPE_T::ARC:
case SHAPE_T::POLY:
if( shape->GetStart() == shape->GetEnd() )
return NULLOPT;
if( aWidth )
*aWidth = shape->GetWidth();
return boost::make_optional<SEG>( { VECTOR2I( shape->GetStart() ),
VECTOR2I( shape->GetEnd() ) } );
default:
return NULLOPT;
}
}
case PCB_TRACE_T:

View File

@ -73,7 +73,7 @@ private:
static OPT<SEG> 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<EDA_ITEM*>& aItems );
SHAPE_POLY_SET makePolysFromChainedSegs( const std::deque<EDA_ITEM*>& 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<EDA_ITEM*>& 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<EDA_ITEM*>& 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<EDA_ITEM*>& aItems );
SHAPE_POLY_SET makePolysFromGraphics( const std::deque<EDA_ITEM*>& aItems );
PCB_SELECTION_TOOL* m_selectionTool;
CONDITIONAL_MENU* m_menu;