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:
parent
bb43d923af
commit
fc1afa6298
|
@ -123,71 +123,29 @@ bool CONVERT_TOOL::Init()
|
||||||
|
|
||||||
int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent )
|
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 )
|
[]( 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() )
|
if( selection.Empty() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
PCB_LAYER_ID destLayer = m_frame->GetActiveLayer();
|
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( makePolysFromGraphics( selection.GetItems() ) );
|
||||||
|
|
||||||
polySet.Append( extractPolygons( selection.GetItems() ) );
|
|
||||||
|
|
||||||
if( polySet.IsEmpty() )
|
if( polySet.IsEmpty() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
polySet.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
bool isFootprint = m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
|
bool isFootprint = m_frame->IsType( FRAME_FOOTPRINT_EDITOR );
|
||||||
|
|
||||||
if( isFootprint )
|
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
|
// For now, we convert each outline in the returned shape to its own polygon
|
||||||
std::vector<SHAPE_POLY_SET> polys;
|
std::vector<SHAPE_POLY_SET> polys;
|
||||||
|
|
||||||
for( int i = 0; i < polySet.OutlineCount(); i++ )
|
for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
|
||||||
polys.emplace_back( SHAPE_POLY_SET( polySet.COutline( i ) ) );
|
{
|
||||||
|
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 ) )
|
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
|
// 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
|
// 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
|
// 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::map<VECTOR2I, std::vector<std::pair<int, EDA_ITEM*>>> connections;
|
||||||
std::set<EDA_ITEM*> used;
|
|
||||||
std::deque<EDA_ITEM*> toCheck;
|
std::deque<EDA_ITEM*> toCheck;
|
||||||
|
|
||||||
auto closeEnough =
|
auto closeEnough =
|
||||||
|
@ -315,6 +277,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
|
||||||
|
|
||||||
for( EDA_ITEM* item : aItems )
|
for( EDA_ITEM* item : aItems )
|
||||||
{
|
{
|
||||||
|
item->ClearFlags( SKIP_STRUCT );
|
||||||
|
|
||||||
if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
|
if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
|
||||||
{
|
{
|
||||||
toCheck.push_back( item );
|
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();
|
EDA_ITEM* candidate = toCheck.front();
|
||||||
toCheck.pop_front();
|
toCheck.pop_front();
|
||||||
|
|
||||||
if( used.count( candidate ) )
|
if( candidate->GetFlags() & SKIP_STRUCT )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int width = -1;
|
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 =
|
std::function<void( EDA_ITEM*, VECTOR2I, bool )> process =
|
||||||
[&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
|
[&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
|
||||||
{
|
{
|
||||||
if( used.count( aItem ) )
|
if( aItem->GetFlags() & SKIP_STRUCT )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
used.insert( aItem );
|
aItem->SetFlags( SKIP_STRUCT );
|
||||||
|
|
||||||
insert( aItem, aAnchor, aDirection );
|
insert( aItem, aAnchor, aDirection );
|
||||||
|
|
||||||
|
@ -446,87 +410,27 @@ 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 )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET poly;
|
BOARD_DESIGN_SETTINGS& bds = m_frame->GetBoard()->GetDesignSettings();
|
||||||
|
SHAPE_POLY_SET poly;
|
||||||
|
|
||||||
for( EDA_ITEM* item : aItems )
|
for( EDA_ITEM* item : aItems )
|
||||||
{
|
{
|
||||||
if( item->Type() != PCB_SHAPE_T && item->Type() != PCB_FP_SHAPE_T )
|
if( item->GetFlags() & SKIP_STRUCT )
|
||||||
continue;
|
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() )
|
switch( item->Type() )
|
||||||
{
|
{
|
||||||
case PCB_SHAPE_T:
|
case PCB_SHAPE_T:
|
||||||
case PCB_FP_SHAPE_T:
|
case PCB_FP_SHAPE_T:
|
||||||
switch( static_cast<PCB_SHAPE*>( item )->GetShape() )
|
{
|
||||||
{
|
PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
|
||||||
case SHAPE_T::POLY:
|
|
||||||
poly.Append( static_cast<PCB_SHAPE*>( item )->GetPolyShape() );
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
graphic->TransformShapeWithClearanceToPolygon( poly, UNDEFINED_LAYER, 0,
|
||||||
|
bds.m_MaxError, ERROR_INSIDE );
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case PCB_ZONE_T:
|
case PCB_ZONE_T:
|
||||||
case PCB_FP_ZONE_T:
|
case PCB_FP_ZONE_T:
|
||||||
|
@ -881,11 +785,23 @@ OPT<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth )
|
||||||
{
|
{
|
||||||
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
|
PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
|
||||||
|
|
||||||
if( aWidth )
|
switch( shape->GetShape() )
|
||||||
*aWidth = shape->GetWidth();
|
{
|
||||||
|
case SHAPE_T::SEGMENT:
|
||||||
|
case SHAPE_T::ARC:
|
||||||
|
case SHAPE_T::POLY:
|
||||||
|
if( shape->GetStart() == shape->GetEnd() )
|
||||||
|
return NULLOPT;
|
||||||
|
|
||||||
return boost::make_optional<SEG>( { VECTOR2I( shape->GetStart() ),
|
if( aWidth )
|
||||||
VECTOR2I( shape->GetEnd() ) } );
|
*aWidth = shape->GetWidth();
|
||||||
|
|
||||||
|
return boost::make_optional<SEG>( { VECTOR2I( shape->GetStart() ),
|
||||||
|
VECTOR2I( shape->GetEnd() ) } );
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NULLOPT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case PCB_TRACE_T:
|
case PCB_TRACE_T:
|
||||||
|
|
|
@ -73,7 +73,7 @@ private:
|
||||||
static OPT<SEG> getStartEndPoints( EDA_ITEM* aItem, int* aWidth );
|
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
|
* 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
|
* 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.
|
* @param aItems is a list of items to process.
|
||||||
* @return a #SHAPE_POLY_SET containing any polygons that were created.
|
* @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.
|
* @return a #SHAPE_POLY_SET containing any polygons that were created.
|
||||||
*/
|
*/
|
||||||
static SHAPE_POLY_SET makePolysFromRects( const std::deque<EDA_ITEM*>& aItems );
|
SHAPE_POLY_SET makePolysFromGraphics( 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 );
|
|
||||||
|
|
||||||
PCB_SELECTION_TOOL* m_selectionTool;
|
PCB_SELECTION_TOOL* m_selectionTool;
|
||||||
CONDITIONAL_MENU* m_menu;
|
CONDITIONAL_MENU* m_menu;
|
||||||
|
|
Loading…
Reference in New Issue