Browse Source

Fix create lines/outset from selection for rrect

Creates a series of lines and arcs based on the requested outset,
iterating over the components.  Also addresses underlying issue with
create lines

Fixes https://gitlab.com/kicad/code/kicad/issues/21601
master
Seth Hillbrand 3 weeks ago
parent
commit
755a14afce
  1. 249
      pcbnew/tools/convert_tool.cpp
  2. 8
      pcbnew/tools/item_modification_routine.cpp

249
pcbnew/tools/convert_tool.cpp

@ -42,6 +42,7 @@
#include <dialogs/dialog_outset_items.h>
#include <footprint.h>
#include <footprint_edit_frame.h>
#include <geometry/roundrect.h>
#include <geometry/shape_compound.h>
#include <pcb_edit_frame.h>
#include <pcb_shape.h>
@ -963,60 +964,6 @@ int CONVERT_TOOL::CreateLines( const TOOL_EVENT& aEvent )
if( selection.Empty() )
return 0;
auto getPolySet =
[]( EDA_ITEM* aItem )
{
SHAPE_POLY_SET set;
switch( aItem->Type() )
{
case PCB_ZONE_T:
set = *static_cast<ZONE*>( aItem )->Outline();
break;
case PCB_SHAPE_T:
{
PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( aItem );
if( graphic->GetShape() == SHAPE_T::RECTANGLE )
{
graphic->TransformShapeToPolygon( set, graphic->GetLayer(), 0, graphic->GetMaxError(), ERROR_INSIDE );
}
else if( graphic->GetShape() == SHAPE_T::POLY )
{
set = graphic->GetPolyShape();
}
else
{
wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines - getPolySet" ) );
}
break;
}
default:
wxFAIL_MSG( wxT( "Unhandled type in PolyToLines - getPolySet" ) );
break;
}
return set;
};
auto getSegList =
[]( SHAPE_POLY_SET& aPoly )
{
std::vector<SEG> segs;
// Our input should be valid polys, so OK to assert here
wxASSERT( aPoly.VertexCount() >= 2 );
for( int i = 1; i < aPoly.VertexCount(); i++ )
segs.emplace_back( SEG( aPoly.CVertex( i - 1 ), aPoly.CVertex( i ) ) );
segs.emplace_back( SEG( aPoly.CVertex( aPoly.VertexCount() - 1 ), aPoly.CVertex( 0 ) ) );
return segs;
};
BOARD_COMMIT commit( m_frame );
PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
FOOTPRINT_EDIT_FRAME* fpEditor = dynamic_cast<FOOTPRINT_EDIT_FRAME*>( m_frame );
@ -1064,6 +1011,129 @@ int CONVERT_TOOL::CreateLines( const TOOL_EVENT& aEvent )
return false;
};
auto addGraphicChain =
[&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth )
{
for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
{
const SEG seg = aChain.GetSegment( si );
if( seg.Length() == 0 )
continue;
if( aChain.IsArcSegment( si ) )
continue;
PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
graphic->SetLayer( targetLayer );
graphic->SetStart( seg.A );
graphic->SetEnd( seg.B );
if( aWidth && *aWidth > 0 )
graphic->SetWidth( *aWidth );
commit.Add( graphic );
}
for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
{
const SHAPE_ARC& arc = aChain.Arc( ai );
if( arc.GetP0() == arc.GetP1() )
continue;
PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::ARC );
graphic->SetLayer( targetLayer );
graphic->SetFilled( false );
graphic->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
if( aWidth && *aWidth > 0 )
graphic->SetWidth( *aWidth );
commit.Add( graphic );
}
};
auto addTrackChain =
[&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth )
{
for( size_t si = 0; si < aChain.GetSegmentCount(); ++si )
{
const SEG seg = aChain.GetSegment( si );
if( seg.Length() == 0 )
continue;
if( aChain.IsArcSegment( si ) )
continue;
PCB_TRACK* track = new PCB_TRACK( parent );
track->SetLayer( targetLayer );
track->SetStart( seg.A );
track->SetEnd( seg.B );
if( aWidth && *aWidth > 0 )
track->SetWidth( *aWidth );
commit.Add( track );
}
for( size_t ai = 0; ai < aChain.ArcCount(); ++ai )
{
const SHAPE_ARC& arc = aChain.Arc( ai );
if( arc.GetP0() == arc.GetP1() )
continue;
PCB_ARC* trackArc = new PCB_ARC( parent );
trackArc->SetLayer( targetLayer );
trackArc->SetStart( arc.GetP0() );
trackArc->SetEnd( arc.GetP1() );
trackArc->SetMid( arc.GetArcMid() );
if( aWidth && *aWidth > 0 )
trackArc->SetWidth( *aWidth );
commit.Add( trackArc );
}
};
auto processChain =
[&]( const SHAPE_LINE_CHAIN& aChain, std::optional<int> aWidth )
{
if( aChain.GetSegmentCount() == 0 && aChain.ArcCount() == 0 )
return;
if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
{
addGraphicChain( aChain, aWidth );
}
else if( fpEditor )
{
addGraphicChain( aChain, aWidth );
}
else
{
addTrackChain( aChain, aWidth );
}
};
auto processPolySet =
[&]( const SHAPE_POLY_SET& aPoly, std::optional<int> aWidth )
{
for( int oi = 0; oi < aPoly.OutlineCount(); ++oi )
{
processChain( aPoly.COutline( oi ), aWidth );
for( int hi = 0; hi < aPoly.HoleCount( oi ); ++hi )
processChain( aPoly.CHole( oi, hi ), aWidth );
}
};
if( aEvent.IsAction( &PCB_ACTIONS::convertToTracks ) )
{
if( !IsCopperLayer( targetLayer ) )
@ -1090,62 +1160,43 @@ int CONVERT_TOOL::CreateLines( const TOOL_EVENT& aEvent )
if( handleGraphicSeg( item ) )
continue;
BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
SHAPE_POLY_SET polySet = getPolySet( item );
std::vector<SEG> segs = getSegList( polySet );
BOARD_ITEM& boardItem = static_cast<BOARD_ITEM&>( *item );
std::optional<int> itemWidth = GetBoardItemWidth( boardItem );
if( aEvent.IsAction( &PCB_ACTIONS::convertToLines ) )
if( boardItem.Type() == PCB_SHAPE_T )
{
for( SEG& seg : segs )
PCB_SHAPE* graphic = static_cast<PCB_SHAPE*>( item );
switch( graphic->GetShape() )
{
case SHAPE_T::RECTANGLE:
{
PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
SHAPE_RECT rect( graphic->GetStart(), graphic->GetEnd() );
ROUNDRECT rrect( rect, graphic->GetCornerRadius(), true );
SHAPE_POLY_SET poly;
graphic->SetLayer( targetLayer );
graphic->SetStart( VECTOR2I( seg.A ) );
graphic->SetEnd( VECTOR2I( seg.B ) );
rrect.TransformToPolygon( poly );
processPolySet( poly, itemWidth );
break;
}
// The width can exist but be 0 for filled, unstroked shapes
if( itemWidth && *itemWidth > 0 )
graphic->SetWidth( *itemWidth );
case SHAPE_T::POLY:
processPolySet( graphic->GetPolyShape(), itemWidth );
break;
commit.Add( graphic );
default:
wxFAIL_MSG( wxT( "Unhandled graphic shape type in PolyToLines" ) );
break;
}
}
else if( boardItem.Type() == PCB_ZONE_T )
{
ZONE* zone = static_cast<ZONE*>( item );
processPolySet( *zone->Outline(), itemWidth );
}
else
{
// I am really unsure converting a polygon to "tracks" (i.e. segments on
// copper layers) make sense for footprints, but anyway this code exists
if( fpEditor )
{
// Creating segments on copper layer
for( SEG& seg : segs )
{
PCB_SHAPE* graphic = new PCB_SHAPE( footprint, SHAPE_T::SEGMENT );
graphic->SetLayer( targetLayer );
graphic->SetStart( VECTOR2I( seg.A ) );
graphic->SetEnd( VECTOR2I( seg.B ) );
if( itemWidth )
graphic->SetWidth( *itemWidth );
commit.Add( graphic );
}
}
else
{
// Creating tracks
for( SEG& seg : segs )
{
PCB_TRACK* track = new PCB_TRACK( parent );
track->SetLayer( targetLayer );
track->SetStart( VECTOR2I( seg.A ) );
track->SetEnd( VECTOR2I( seg.B ) );
commit.Add( track );
}
}
wxFAIL_MSG( wxT( "Unhandled type in PolyToLines" ) );
}
}

8
pcbnew/tools/item_modification_routine.cpp

@ -983,13 +983,19 @@ void OUTSET_ROUTINE::ProcessItem( BOARD_ITEM& aItem )
VECTOR2I{ pcb_shape.GetRectangleWidth(), pcb_shape.GetRectangleHeight() } };
box.Inflate( m_params.outsetDistance );
box.Normalize();
SHAPE_RECT rect( box );
if( m_params.roundCorners )
{
if( m_params.gridRounding.has_value() )
rect = GetRectRoundedToGridOutwards( rect, *m_params.gridRounding );
try
{
ROUNDRECT rrect( rect, m_params.outsetDistance );
int cornerRadius = pcb_shape.GetCornerRadius() + m_params.outsetDistance;
ROUNDRECT rrect( rect, cornerRadius );
SHAPE_POLY_SET poly;
rrect.TransformToPolygon( poly );
addPoly( poly );

Loading…
Cancel
Save