ADDED: Convert tool can create polygons with arcs from contiguous line and arc segments
Fixes https://gitlab.com/kicad/code/kicad/-/issues/5409
This commit is contained in:
parent
576d70fab0
commit
b291f36dae
|
@ -23,24 +23,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <bitmaps.h>
|
#include <bitmaps.h>
|
||||||
#include <board_commit.h>
|
|
||||||
#include <board.h>
|
#include <board.h>
|
||||||
|
#include <board_commit.h>
|
||||||
#include <board_design_settings.h>
|
#include <board_design_settings.h>
|
||||||
#include <pcb_shape.h>
|
|
||||||
#include <fp_shape.h>
|
|
||||||
#include <pcb_track.h>
|
|
||||||
#include <zone.h>
|
|
||||||
#include <collectors.h>
|
#include <collectors.h>
|
||||||
#include <confirm.h>
|
#include <confirm.h>
|
||||||
|
#include <convert_basic_shapes_to_polygon.h>
|
||||||
|
#include <footprint_edit_frame.h>
|
||||||
|
#include <fp_shape.h>
|
||||||
|
#include <geometry/shape_compound.h>
|
||||||
#include <menus_helpers.h>
|
#include <menus_helpers.h>
|
||||||
#include <pcb_edit_frame.h>
|
#include <pcb_edit_frame.h>
|
||||||
#include <footprint_edit_frame.h>
|
#include <pcb_shape.h>
|
||||||
#include <trigo.h>
|
#include <pcb_track.h>
|
||||||
#include <tool/tool_manager.h>
|
#include <tool/tool_manager.h>
|
||||||
#include <tools/edit_tool.h>
|
#include <tools/edit_tool.h>
|
||||||
#include <tools/pcb_actions.h>
|
#include <tools/pcb_actions.h>
|
||||||
#include <tools/pcb_selection_tool.h>
|
#include <tools/pcb_selection_tool.h>
|
||||||
#include <convert_basic_shapes_to_polygon.h>
|
#include <trigo.h>
|
||||||
|
#include <zone.h>
|
||||||
|
|
||||||
#include "convert_tool.h"
|
#include "convert_tool.h"
|
||||||
|
|
||||||
|
@ -73,14 +74,16 @@ bool CONVERT_TOOL::Init()
|
||||||
m_menu->SetIcon( BITMAPS::convert );
|
m_menu->SetIcon( BITMAPS::convert );
|
||||||
m_menu->SetTitle( _( "Convert" ) );
|
m_menu->SetTitle( _( "Convert" ) );
|
||||||
|
|
||||||
static KICAD_T convertableTracks[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
|
static KICAD_T convertibleTracks[] = { PCB_TRACE_T, PCB_ARC_T, EOT };
|
||||||
static KICAD_T zones[] = { PCB_ZONE_T, PCB_FP_ZONE_T, EOT };
|
static KICAD_T zones[] = { PCB_ZONE_T, PCB_FP_ZONE_T, EOT };
|
||||||
|
|
||||||
auto graphicLines = P_S_C::OnlyGraphicShapeTypes( { PCB_SHAPE_TYPE::SEGMENT, PCB_SHAPE_TYPE::RECT,
|
auto graphicLines = P_S_C::OnlyGraphicShapeTypes( { PCB_SHAPE_TYPE::SEGMENT,
|
||||||
PCB_SHAPE_TYPE::CIRCLE } )
|
PCB_SHAPE_TYPE::RECT,
|
||||||
|
PCB_SHAPE_TYPE::CIRCLE,
|
||||||
|
PCB_SHAPE_TYPE::ARC } )
|
||||||
&& P_S_C::SameLayer();
|
&& P_S_C::SameLayer();
|
||||||
|
|
||||||
auto trackLines = S_C::MoreThan( 1 ) && S_C::OnlyTypes( convertableTracks )
|
auto trackLines = S_C::MoreThan( 1 ) && S_C::OnlyTypes( convertibleTracks )
|
||||||
&& P_S_C::SameLayer();
|
&& P_S_C::SameLayer();
|
||||||
|
|
||||||
auto anyLines = graphicLines || trackLines;
|
auto anyLines = graphicLines || trackLines;
|
||||||
|
@ -134,7 +137,7 @@ int CONVERT_TOOL::LinesToPoly( const TOOL_EVENT& aEvent )
|
||||||
case PCB_SHAPE_TYPE::SEGMENT:
|
case PCB_SHAPE_TYPE::SEGMENT:
|
||||||
case PCB_SHAPE_TYPE::RECT:
|
case PCB_SHAPE_TYPE::RECT:
|
||||||
case PCB_SHAPE_TYPE::CIRCLE:
|
case PCB_SHAPE_TYPE::CIRCLE:
|
||||||
// case S_ARC: // Not yet
|
case PCB_SHAPE_TYPE::ARC:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -144,7 +147,7 @@ int CONVERT_TOOL::LinesToPoly( const TOOL_EVENT& aEvent )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCB_TRACE_T:
|
case PCB_TRACE_T:
|
||||||
// case PCB_ARC_T: // Not yet
|
case PCB_ARC_T:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -241,7 +244,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET poly;
|
SHAPE_POLY_SET poly;
|
||||||
|
|
||||||
std::map<VECTOR2I, std::vector<EDA_ITEM*>> connections;
|
// 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::set<EDA_ITEM*> used;
|
||||||
std::deque<EDA_ITEM*> toCheck;
|
std::deque<EDA_ITEM*> toCheck;
|
||||||
|
|
||||||
|
@ -250,8 +254,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
|
||||||
if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
|
if( OPT<SEG> seg = getStartEndPoints( item, nullptr ) )
|
||||||
{
|
{
|
||||||
toCheck.push_back( item );
|
toCheck.push_back( item );
|
||||||
connections[seg->A].emplace_back( item );
|
connections[seg->A].emplace_back( std::make_pair( 0, item ) );
|
||||||
connections[seg->B].emplace_back( item );
|
connections[seg->B].emplace_back( std::make_pair( 1, item ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,67 +267,103 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque<EDA_ITEM*>& aIt
|
||||||
if( used.count( candidate ) )
|
if( used.count( candidate ) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int width = -1;
|
int width = -1;
|
||||||
OPT<SEG> seg = getStartEndPoints( candidate, &width );
|
SHAPE_LINE_CHAIN outline;
|
||||||
wxASSERT( seg );
|
|
||||||
|
|
||||||
SHAPE_LINE_CHAIN outline;
|
auto insert =
|
||||||
std::deque<VECTOR2I> points;
|
[&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
|
||||||
|
{
|
||||||
|
if( aItem->Type() == PCB_ARC_T ||
|
||||||
|
( aItem->Type() == PCB_SHAPE_T &&
|
||||||
|
static_cast<PCB_SHAPE*>( aItem )->GetShape() == PCB_SHAPE_TYPE::ARC ) )
|
||||||
|
{
|
||||||
|
SHAPE_ARC arc;
|
||||||
|
|
||||||
|
if( aItem->Type() == PCB_ARC_T )
|
||||||
|
{
|
||||||
|
std::shared_ptr<SHAPE> es =
|
||||||
|
static_cast<PCB_ARC*>( aItem )->GetEffectiveShape();
|
||||||
|
arc = *static_cast<SHAPE_ARC*>( es.get() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PCB_SHAPE* ps = static_cast<PCB_SHAPE*>( aItem );
|
||||||
|
arc = SHAPE_ARC( ps->GetArcStart(), ps->GetArcMid(), ps->GetArcEnd(),
|
||||||
|
ps->GetWidth() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( aDirection )
|
||||||
|
outline.Append( aAnchor == arc.GetP0() ? arc : arc.Reversed() );
|
||||||
|
else
|
||||||
|
outline.Insert( 0, aAnchor == arc.GetP0() ? arc : arc.Reversed() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OPT<SEG> nextSeg = getStartEndPoints( aItem, &width );
|
||||||
|
wxASSERT( nextSeg );
|
||||||
|
|
||||||
|
VECTOR2I& point = ( aAnchor == nextSeg->A ) ? nextSeg->B : nextSeg->A;
|
||||||
|
|
||||||
|
if( aDirection )
|
||||||
|
outline.Append( point );
|
||||||
|
else
|
||||||
|
outline.Insert( 0, point );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// aDirection == true for walking "right" and appending to the end of points
|
// aDirection == true for walking "right" and appending to the end of points
|
||||||
// false for walking "left" and prepending to the beginning
|
// false for walking "left" and prepending to the beginning
|
||||||
std::function<void( EDA_ITEM*, bool )> process =
|
std::function<void( EDA_ITEM*, VECTOR2I, bool )> process =
|
||||||
[&]( EDA_ITEM* aItem, bool aDirection )
|
[&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection )
|
||||||
{
|
{
|
||||||
if( used.count( aItem ) )
|
if( used.count( aItem ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
used.insert( aItem );
|
used.insert( aItem );
|
||||||
|
|
||||||
OPT<SEG> nextSeg = getStartEndPoints( aItem, &width );
|
insert( aItem, aAnchor, aDirection );
|
||||||
wxASSERT( nextSeg );
|
|
||||||
|
|
||||||
// The reference point, i.e. last added point in the direction we're headed
|
OPT<SEG> anchors = getStartEndPoints( aItem, &width );
|
||||||
VECTOR2I& ref = aDirection ? points.back() : points.front();
|
wxASSERT( anchors );
|
||||||
|
|
||||||
// The next point, i.e. the other point on this segment
|
VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A;
|
||||||
VECTOR2I& next = ( ref == nextSeg->A ) ? nextSeg->B : nextSeg->A;
|
|
||||||
|
|
||||||
if( aDirection )
|
for( std::pair<int, EDA_ITEM*> pair : connections[nextAnchor] )
|
||||||
points.push_back( next );
|
{
|
||||||
else
|
if( pair.second == aItem )
|
||||||
points.push_front( next );
|
continue;
|
||||||
|
|
||||||
for( EDA_ITEM* neighbor : connections[next] )
|
process( pair.second, nextAnchor, aDirection );
|
||||||
process( neighbor, aDirection );
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Start with just one point and walk one direction
|
OPT<SEG> anchors = getStartEndPoints( candidate, &width );
|
||||||
points.push_back( seg->A );
|
wxASSERT( anchors );
|
||||||
process( candidate, true );
|
|
||||||
|
// Start with the first object and walk "right"
|
||||||
|
insert( candidate, anchors->A, true );
|
||||||
|
process( candidate, anchors->B, true );
|
||||||
|
|
||||||
// check for any candidates on the "left"
|
// check for any candidates on the "left"
|
||||||
EDA_ITEM* left = nullptr;
|
EDA_ITEM* left = nullptr;
|
||||||
|
|
||||||
for( EDA_ITEM* possibleLeft : connections[seg->A] )
|
for( std::pair<int, EDA_ITEM*> possibleLeft : connections[anchors->A] )
|
||||||
{
|
{
|
||||||
if( possibleLeft != candidate )
|
if( possibleLeft.second != candidate )
|
||||||
{
|
{
|
||||||
left = possibleLeft;
|
left = possibleLeft.second;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( left )
|
if( left )
|
||||||
process( left, false );
|
process( left, anchors->A, false );
|
||||||
|
|
||||||
if( points.size() < 3 )
|
if( outline.PointCount() < 3 )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for( const VECTOR2I& point : points )
|
|
||||||
outline.Append( point );
|
|
||||||
|
|
||||||
outline.SetClosed( true );
|
outline.SetClosed( true );
|
||||||
|
outline.Simplify();
|
||||||
|
|
||||||
if( width >= 0 )
|
if( width >= 0 )
|
||||||
outline.SetWidth( width );
|
outline.SetWidth( width );
|
||||||
|
@ -691,8 +731,16 @@ OPT<SEG> CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth )
|
||||||
if( aWidth )
|
if( aWidth )
|
||||||
*aWidth = line->GetWidth();
|
*aWidth = line->GetWidth();
|
||||||
|
|
||||||
return boost::make_optional<SEG>( { VECTOR2I( line->GetStart() ),
|
if( line->GetShape() == PCB_SHAPE_TYPE::SEGMENT )
|
||||||
VECTOR2I( line->GetEnd() ) } );
|
{
|
||||||
|
return boost::make_optional<SEG>( { VECTOR2I( line->GetStart() ),
|
||||||
|
VECTOR2I( line->GetEnd() ) } );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return boost::make_optional<SEG>( { VECTOR2I( line->GetArcStart() ),
|
||||||
|
VECTOR2I( line->GetArcEnd() ) } );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case PCB_TRACE_T:
|
case PCB_TRACE_T:
|
||||||
|
|
Loading…
Reference in New Issue