From b291f36dae66a9e5d0c78b6ac8c3153664ac62c4 Mon Sep 17 00:00:00 2001 From: Jon Evans Date: Sun, 4 Jul 2021 12:09:37 -0400 Subject: [PATCH] ADDED: Convert tool can create polygons with arcs from contiguous line and arc segments Fixes https://gitlab.com/kicad/code/kicad/-/issues/5409 --- pcbnew/tools/convert_tool.cpp | 146 ++++++++++++++++++++++------------ 1 file changed, 97 insertions(+), 49 deletions(-) diff --git a/pcbnew/tools/convert_tool.cpp b/pcbnew/tools/convert_tool.cpp index e0f23ae8fd..c519e3d355 100644 --- a/pcbnew/tools/convert_tool.cpp +++ b/pcbnew/tools/convert_tool.cpp @@ -23,24 +23,25 @@ */ #include -#include #include +#include #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include #include #include -#include -#include +#include +#include #include #include #include #include -#include +#include +#include #include "convert_tool.h" @@ -73,14 +74,16 @@ bool CONVERT_TOOL::Init() m_menu->SetIcon( BITMAPS::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 }; - auto graphicLines = P_S_C::OnlyGraphicShapeTypes( { PCB_SHAPE_TYPE::SEGMENT, PCB_SHAPE_TYPE::RECT, - PCB_SHAPE_TYPE::CIRCLE } ) + auto graphicLines = P_S_C::OnlyGraphicShapeTypes( { PCB_SHAPE_TYPE::SEGMENT, + PCB_SHAPE_TYPE::RECT, + PCB_SHAPE_TYPE::CIRCLE, + PCB_SHAPE_TYPE::ARC } ) && 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(); 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::RECT: case PCB_SHAPE_TYPE::CIRCLE: - // case S_ARC: // Not yet + case PCB_SHAPE_TYPE::ARC: break; default: @@ -144,7 +147,7 @@ int CONVERT_TOOL::LinesToPoly( const TOOL_EVENT& aEvent ) break; case PCB_TRACE_T: - // case PCB_ARC_T: // Not yet + case PCB_ARC_T: break; default: @@ -241,7 +244,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt { SHAPE_POLY_SET poly; - std::map> connections; + // Stores pairs of (anchor, item) where anchor == 0 -> SEG.A, anchor == 1 -> SEG.B + std::map>> connections; std::set used; std::deque toCheck; @@ -250,8 +254,8 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt if( OPT seg = getStartEndPoints( item, nullptr ) ) { toCheck.push_back( item ); - connections[seg->A].emplace_back( item ); - connections[seg->B].emplace_back( item ); + connections[seg->A].emplace_back( std::make_pair( 0, item ) ); + connections[seg->B].emplace_back( std::make_pair( 1, item ) ); } } @@ -263,67 +267,103 @@ SHAPE_POLY_SET CONVERT_TOOL::makePolysFromSegs( const std::deque& aIt if( used.count( candidate ) ) continue; - int width = -1; - OPT seg = getStartEndPoints( candidate, &width ); - wxASSERT( seg ); + int width = -1; + SHAPE_LINE_CHAIN outline; - SHAPE_LINE_CHAIN outline; - std::deque points; + auto insert = + [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection ) + { + if( aItem->Type() == PCB_ARC_T || + ( aItem->Type() == PCB_SHAPE_T && + static_cast( aItem )->GetShape() == PCB_SHAPE_TYPE::ARC ) ) + { + SHAPE_ARC arc; + + if( aItem->Type() == PCB_ARC_T ) + { + std::shared_ptr es = + static_cast( aItem )->GetEffectiveShape(); + arc = *static_cast( es.get() ); + } + else + { + PCB_SHAPE* ps = static_cast( 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 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 // false for walking "left" and prepending to the beginning - std::function process = - [&]( EDA_ITEM* aItem, bool aDirection ) + std::function process = + [&]( EDA_ITEM* aItem, VECTOR2I aAnchor, bool aDirection ) { if( used.count( aItem ) ) return; used.insert( aItem ); - OPT nextSeg = getStartEndPoints( aItem, &width ); - wxASSERT( nextSeg ); + insert( aItem, aAnchor, aDirection ); - // The reference point, i.e. last added point in the direction we're headed - VECTOR2I& ref = aDirection ? points.back() : points.front(); + OPT anchors = getStartEndPoints( aItem, &width ); + wxASSERT( anchors ); - // The next point, i.e. the other point on this segment - VECTOR2I& next = ( ref == nextSeg->A ) ? nextSeg->B : nextSeg->A; + VECTOR2I nextAnchor = ( aAnchor == anchors->A ) ? anchors->B : anchors->A; - if( aDirection ) - points.push_back( next ); - else - points.push_front( next ); + for( std::pair pair : connections[nextAnchor] ) + { + if( pair.second == aItem ) + continue; - for( EDA_ITEM* neighbor : connections[next] ) - process( neighbor, aDirection ); + process( pair.second, nextAnchor, aDirection ); + } }; - // Start with just one point and walk one direction - points.push_back( seg->A ); - process( candidate, true ); + OPT anchors = getStartEndPoints( candidate, &width ); + wxASSERT( anchors ); + + // 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" EDA_ITEM* left = nullptr; - for( EDA_ITEM* possibleLeft : connections[seg->A] ) + for( std::pair possibleLeft : connections[anchors->A] ) { - if( possibleLeft != candidate ) + if( possibleLeft.second != candidate ) { - left = possibleLeft; + left = possibleLeft.second; break; } } if( left ) - process( left, false ); + process( left, anchors->A, false ); - if( points.size() < 3 ) + if( outline.PointCount() < 3 ) continue; - for( const VECTOR2I& point : points ) - outline.Append( point ); - outline.SetClosed( true ); + outline.Simplify(); if( width >= 0 ) outline.SetWidth( width ); @@ -691,8 +731,16 @@ OPT CONVERT_TOOL::getStartEndPoints( EDA_ITEM* aItem, int* aWidth ) if( aWidth ) *aWidth = line->GetWidth(); - return boost::make_optional( { VECTOR2I( line->GetStart() ), - VECTOR2I( line->GetEnd() ) } ); + if( line->GetShape() == PCB_SHAPE_TYPE::SEGMENT ) + { + return boost::make_optional( { VECTOR2I( line->GetStart() ), + VECTOR2I( line->GetEnd() ) } ); + } + else + { + return boost::make_optional( { VECTOR2I( line->GetArcStart() ), + VECTOR2I( line->GetArcEnd() ) } ); + } } case PCB_TRACE_T: