Fix fillet action for lines
Generalize fillet action behavior to all straight lines in pcbnew Fixes https://gitlab.com/kicad/code/kicad/issues/4742
This commit is contained in:
parent
7b1702b5fb
commit
01138a2e19
|
@ -53,6 +53,7 @@
|
||||||
#include <view/view_controls.h>
|
#include <view/view_controls.h>
|
||||||
#include <connectivity/connectivity_algo.h>
|
#include <connectivity/connectivity_algo.h>
|
||||||
#include <connectivity/connectivity_items.h>
|
#include <connectivity/connectivity_items.h>
|
||||||
|
#include <core/kicad_algo.h>
|
||||||
#include <bitmaps.h>
|
#include <bitmaps.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -180,6 +181,10 @@ bool EDIT_TOOL::Init()
|
||||||
PCB_ARC_T,
|
PCB_ARC_T,
|
||||||
PCB_VIA_T };
|
PCB_VIA_T };
|
||||||
|
|
||||||
|
static std::vector<KICAD_T> filletTypes = { PCB_SHAPE_LOCATE_POLY_T,
|
||||||
|
PCB_SHAPE_LOCATE_RECT_T,
|
||||||
|
PCB_SHAPE_LOCATE_SEGMENT_T };
|
||||||
|
|
||||||
|
|
||||||
// Add context menu entries that are displayed when selection tool is active
|
// Add context menu entries that are displayed when selection tool is active
|
||||||
CONDITIONAL_MENU& menu = m_selectionTool->GetToolMenu().GetMenu();
|
CONDITIONAL_MENU& menu = m_selectionTool->GetToolMenu().GetMenu();
|
||||||
|
@ -198,6 +203,7 @@ bool EDIT_TOOL::Init()
|
||||||
&& SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::DraggableItems )
|
&& SELECTION_CONDITIONS::OnlyTypes( GENERAL_COLLECTOR::DraggableItems )
|
||||||
&& !SELECTION_CONDITIONS::OnlyTypes( { PCB_FOOTPRINT_T } ) );
|
&& !SELECTION_CONDITIONS::OnlyTypes( { PCB_FOOTPRINT_T } ) );
|
||||||
menu.AddItem( PCB_ACTIONS::filletTracks, SELECTION_CONDITIONS::OnlyTypes( trackTypes ) );
|
menu.AddItem( PCB_ACTIONS::filletTracks, SELECTION_CONDITIONS::OnlyTypes( trackTypes ) );
|
||||||
|
menu.AddItem( PCB_ACTIONS::filletLines, SELECTION_CONDITIONS::OnlyTypes( filletTypes ) );
|
||||||
menu.AddItem( PCB_ACTIONS::rotateCcw, SELECTION_CONDITIONS::NotEmpty );
|
menu.AddItem( PCB_ACTIONS::rotateCcw, SELECTION_CONDITIONS::NotEmpty );
|
||||||
menu.AddItem( PCB_ACTIONS::rotateCw, SELECTION_CONDITIONS::NotEmpty );
|
menu.AddItem( PCB_ACTIONS::rotateCw, SELECTION_CONDITIONS::NotEmpty );
|
||||||
menu.AddItem( PCB_ACTIONS::flip, SELECTION_CONDITIONS::NotEmpty );
|
menu.AddItem( PCB_ACTIONS::flip, SELECTION_CONDITIONS::NotEmpty );
|
||||||
|
@ -1026,6 +1032,280 @@ int EDIT_TOOL::FilletTracks( const TOOL_EVENT& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int EDIT_TOOL::FilletLines( const TOOL_EVENT& aEvent )
|
||||||
|
{
|
||||||
|
// Store last used fillet radius to allow pressing "enter" if repeat fillet is required
|
||||||
|
static long long filletRadiusIU = 0;
|
||||||
|
|
||||||
|
PCB_SELECTION& selection = m_selectionTool->RequestSelection(
|
||||||
|
[]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<VECTOR2I> pts;
|
||||||
|
|
||||||
|
// Iterate from the back so we don't have to worry about removals.
|
||||||
|
for( int i = aCollector.GetCount() - 1; i >= 0; --i )
|
||||||
|
{
|
||||||
|
BOARD_ITEM* item = aCollector[i];
|
||||||
|
|
||||||
|
// We've converted the polygon and rectangle to segments, so drop everything
|
||||||
|
// that isn't a segment at this point
|
||||||
|
if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T,
|
||||||
|
PCB_SHAPE_LOCATE_POLY_T,
|
||||||
|
PCB_SHAPE_LOCATE_RECT_T } ) )
|
||||||
|
{
|
||||||
|
aCollector.Remove( item );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true /* prompt user regarding locked items */ );
|
||||||
|
|
||||||
|
std::set<PCB_SHAPE*> lines_to_add;
|
||||||
|
std::vector<PCB_SHAPE*> items_to_remove;
|
||||||
|
|
||||||
|
for( EDA_ITEM* item : selection )
|
||||||
|
{
|
||||||
|
std::vector<VECTOR2I> pts;
|
||||||
|
PCB_SHAPE *graphic = static_cast<PCB_SHAPE*>( item );
|
||||||
|
|
||||||
|
if( graphic->GetShape() == SHAPE_T::RECT )
|
||||||
|
{
|
||||||
|
items_to_remove.push_back( graphic );
|
||||||
|
VECTOR2I start( graphic->GetStart() );
|
||||||
|
VECTOR2I end( graphic->GetEnd() );
|
||||||
|
pts.emplace_back( start );
|
||||||
|
pts.emplace_back( VECTOR2I( end.x, start.y ) );
|
||||||
|
pts.emplace_back( end );
|
||||||
|
pts.emplace_back( VECTOR2I( start.x, end.y ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( graphic->GetShape() == SHAPE_T::POLY )
|
||||||
|
{
|
||||||
|
items_to_remove.push_back( graphic );
|
||||||
|
|
||||||
|
for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
|
||||||
|
pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( size_t jj = 1; jj < pts.size(); ++jj )
|
||||||
|
{
|
||||||
|
PCB_SHAPE *line;
|
||||||
|
|
||||||
|
if( m_isFootprintEditor )
|
||||||
|
line = new FP_SHAPE( static_cast<FOOTPRINT*>( frame()->GetModel() ), SHAPE_T::SEGMENT );
|
||||||
|
else
|
||||||
|
line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
|
||||||
|
|
||||||
|
line->SetStart( pts[jj - 1] );
|
||||||
|
line->SetEnd( pts[jj] );
|
||||||
|
lines_to_add.insert( line );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pts.size() > 1 )
|
||||||
|
{
|
||||||
|
PCB_SHAPE *line;
|
||||||
|
|
||||||
|
if( m_isFootprintEditor )
|
||||||
|
line = new FP_SHAPE( static_cast<FOOTPRINT*>( frame()->GetModel() ), SHAPE_T::SEGMENT );
|
||||||
|
else
|
||||||
|
line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
|
||||||
|
|
||||||
|
line->SetStart( pts.back() );
|
||||||
|
line->SetEnd( pts.front() );
|
||||||
|
lines_to_add.insert( line );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for( PCB_SHAPE* item : lines_to_add )
|
||||||
|
selection.Add( item );
|
||||||
|
|
||||||
|
for( PCB_SHAPE* item : items_to_remove )
|
||||||
|
selection.Remove( item );
|
||||||
|
|
||||||
|
if( selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) < 2 )
|
||||||
|
{
|
||||||
|
frame()->ShowInfoBarMsg( _( "A shape with least two lines must be selected." ) );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WX_UNIT_ENTRY_DIALOG dia( frame(), _( "Enter fillet radius:" ), _( "Fillet Lines" ),
|
||||||
|
filletRadiusIU );
|
||||||
|
|
||||||
|
if( dia.ShowModal() == wxID_CANCEL )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
filletRadiusIU = dia.GetValue();
|
||||||
|
|
||||||
|
if( filletRadiusIU == 0 )
|
||||||
|
{
|
||||||
|
frame()->ShowInfoBarMsg( _( "A radius of zero was entered.\n"
|
||||||
|
"The fillet operation was not performed." ) );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operationPerformedOnAtLeastOne = false;
|
||||||
|
bool didOneAttemptFail = false;
|
||||||
|
std::vector<BOARD_ITEM*> itemsToAddToSelection;
|
||||||
|
|
||||||
|
// Only modify one parent in FP editor
|
||||||
|
if( m_isFootprintEditor )
|
||||||
|
m_commit->Modify( selection.Front() );
|
||||||
|
|
||||||
|
alg::for_all_pairs( selection.begin(), selection.end(), [&]( EDA_ITEM* a, EDA_ITEM* b )
|
||||||
|
{
|
||||||
|
PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
|
||||||
|
PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
|
||||||
|
|
||||||
|
if( line_a->GetLength() == 0.0 || line_b->GetLength() == 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
SEG seg_a( line_a->GetStart(), line_a->GetEnd() );
|
||||||
|
SEG seg_b( line_b->GetStart(), line_b->GetEnd() );
|
||||||
|
VECTOR2I* a_pt;
|
||||||
|
VECTOR2I* b_pt;
|
||||||
|
|
||||||
|
if (seg_a.A == seg_b.A)
|
||||||
|
{
|
||||||
|
a_pt = &seg_a.A;
|
||||||
|
b_pt = &seg_b.A;
|
||||||
|
}
|
||||||
|
else if (seg_a.A == seg_b.B)
|
||||||
|
{
|
||||||
|
a_pt = &seg_a.A;
|
||||||
|
b_pt = &seg_b.B;
|
||||||
|
}
|
||||||
|
else if (seg_a.B == seg_b.A)
|
||||||
|
{
|
||||||
|
a_pt = &seg_a.B;
|
||||||
|
b_pt = &seg_b.A;
|
||||||
|
}
|
||||||
|
else if (seg_a.B == seg_b.B)
|
||||||
|
{
|
||||||
|
a_pt = &seg_a.B;
|
||||||
|
b_pt = &seg_b.B;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
SHAPE_ARC sArc( seg_a, seg_b, filletRadiusIU );
|
||||||
|
VECTOR2I t1newPoint, t2newPoint;
|
||||||
|
|
||||||
|
auto setIfPointOnSeg =
|
||||||
|
[]( VECTOR2I& aPointToSet, SEG aSegment, VECTOR2I aVecToTest )
|
||||||
|
{
|
||||||
|
VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
|
||||||
|
|
||||||
|
// Find out if we are on the segment (minimum precision)
|
||||||
|
if( segToVec.EuclideanNorm() < SHAPE_ARC::MIN_PRECISION_IU )
|
||||||
|
{
|
||||||
|
aPointToSet.x = aVecToTest.x;
|
||||||
|
aPointToSet.y = aVecToTest.y;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Do not draw a fillet if the end points of the arc are not within the track segments
|
||||||
|
if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP0() )
|
||||||
|
&& !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP0() ) )
|
||||||
|
{
|
||||||
|
didOneAttemptFail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !setIfPointOnSeg( t1newPoint, seg_a, sArc.GetP1() )
|
||||||
|
&& !setIfPointOnSeg( t2newPoint, seg_b, sArc.GetP1() ) )
|
||||||
|
{
|
||||||
|
didOneAttemptFail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCB_SHAPE* tArc;
|
||||||
|
|
||||||
|
if( m_isFootprintEditor )
|
||||||
|
tArc = new FP_SHAPE( static_cast<FOOTPRINT*>( frame()->GetModel() ), SHAPE_T::ARC );
|
||||||
|
else
|
||||||
|
tArc = new PCB_SHAPE( frame()->GetBoard(), SHAPE_T::ARC );
|
||||||
|
|
||||||
|
tArc->SetArcGeometry( sArc.GetP0(), sArc.GetArcMid(), sArc.GetP1() );
|
||||||
|
tArc->SetWidth( line_a->GetWidth() );
|
||||||
|
tArc->SetLayer( line_a->GetLayer() );
|
||||||
|
tArc->SetLocked( line_a->IsLocked() );
|
||||||
|
|
||||||
|
if( lines_to_add.count( line_a ) )
|
||||||
|
{
|
||||||
|
lines_to_add.erase( line_a );
|
||||||
|
itemsToAddToSelection.push_back( line_a );
|
||||||
|
}
|
||||||
|
else if( !m_isFootprintEditor )
|
||||||
|
{
|
||||||
|
m_commit->Modify( line_a );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( lines_to_add.count( line_b ) )
|
||||||
|
{
|
||||||
|
lines_to_add.erase( line_b );
|
||||||
|
itemsToAddToSelection.push_back( line_b );
|
||||||
|
}
|
||||||
|
else if( !m_isFootprintEditor )
|
||||||
|
{
|
||||||
|
m_commit->Modify( line_b );
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsToAddToSelection.push_back( tArc );
|
||||||
|
*a_pt = t1newPoint;
|
||||||
|
*b_pt = t2newPoint;
|
||||||
|
line_a->SetStart( seg_a.A );
|
||||||
|
line_a->SetEnd( seg_a.B );
|
||||||
|
line_b->SetStart( seg_b.A );
|
||||||
|
line_b->SetEnd( seg_b.B );
|
||||||
|
|
||||||
|
if( m_isFootprintEditor )
|
||||||
|
{
|
||||||
|
static_cast<FP_SHAPE*>( line_a )->SetLocalCoord();
|
||||||
|
static_cast<FP_SHAPE*>( line_b )->SetLocalCoord();
|
||||||
|
static_cast<FP_SHAPE*>( tArc )->SetLocalCoord();
|
||||||
|
}
|
||||||
|
|
||||||
|
operationPerformedOnAtLeastOne = true;
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
for( auto item : items_to_remove )
|
||||||
|
{
|
||||||
|
m_commit->Remove( item );
|
||||||
|
m_selectionTool->RemoveItemFromSel( item, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
//select the newly created arcs
|
||||||
|
for( BOARD_ITEM* item : itemsToAddToSelection )
|
||||||
|
{
|
||||||
|
m_commit->Add( item );
|
||||||
|
m_selectionTool->AddItemToSel( item, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !items_to_remove.empty() )
|
||||||
|
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
|
||||||
|
|
||||||
|
if( !itemsToAddToSelection.empty() )
|
||||||
|
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
|
||||||
|
|
||||||
|
// Notify other tools of the changes
|
||||||
|
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
|
||||||
|
|
||||||
|
m_commit->Push( _( "Fillet Lines" ) );
|
||||||
|
|
||||||
|
if( !operationPerformedOnAtLeastOne )
|
||||||
|
frame()->ShowInfoBarMsg( _( "Unable to fillet the selected lines." ) );
|
||||||
|
else if( didOneAttemptFail )
|
||||||
|
frame()->ShowInfoBarMsg( _( "Some of the lines could not be filleted." ) );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
|
int EDIT_TOOL::Properties( const TOOL_EVENT& aEvent )
|
||||||
{
|
{
|
||||||
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
|
||||||
|
@ -2280,6 +2560,7 @@ void EDIT_TOOL::setTransitions()
|
||||||
Go( &EDIT_TOOL::PackAndMoveFootprints, PCB_ACTIONS::packAndMoveFootprints.MakeEvent() );
|
Go( &EDIT_TOOL::PackAndMoveFootprints, PCB_ACTIONS::packAndMoveFootprints.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::ChangeTrackWidth, PCB_ACTIONS::changeTrackWidth.MakeEvent() );
|
Go( &EDIT_TOOL::ChangeTrackWidth, PCB_ACTIONS::changeTrackWidth.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::FilletTracks, PCB_ACTIONS::filletTracks.MakeEvent() );
|
Go( &EDIT_TOOL::FilletTracks, PCB_ACTIONS::filletTracks.MakeEvent() );
|
||||||
|
Go( &EDIT_TOOL::FilletLines, PCB_ACTIONS::filletLines.MakeEvent() );
|
||||||
|
|
||||||
Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() );
|
Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() );
|
||||||
Go( &EDIT_TOOL::copyToClipboard, PCB_ACTIONS::copyWithReference.MakeEvent() );
|
Go( &EDIT_TOOL::copyToClipboard, PCB_ACTIONS::copyWithReference.MakeEvent() );
|
||||||
|
|
|
@ -128,6 +128,11 @@ public:
|
||||||
*/
|
*/
|
||||||
int FilletTracks( const TOOL_EVENT& aEvent );
|
int FilletTracks( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fillet (i.e. adds an arc tangent to) all selected straight lines by a user defined radius.
|
||||||
|
*/
|
||||||
|
int FilletLines( const TOOL_EVENT& aEvent );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete currently selected items.
|
* Delete currently selected items.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -357,6 +357,10 @@ TOOL_ACTION PCB_ACTIONS::filletTracks( "pcbnew.InteractiveEdit.filletTracks",
|
||||||
AS_GLOBAL, 0, "",
|
AS_GLOBAL, 0, "",
|
||||||
_( "Fillet Tracks" ), _( "Adds arcs tangent to the selected straight track segments" ) );
|
_( "Fillet Tracks" ), _( "Adds arcs tangent to the selected straight track segments" ) );
|
||||||
|
|
||||||
|
TOOL_ACTION PCB_ACTIONS::filletLines( "pcbnew.InteractiveEdit.filletLines",
|
||||||
|
AS_GLOBAL, 0, "",
|
||||||
|
_( "Fillet Lines" ), _( "Adds arcs tangent to the selected lines" ) );
|
||||||
|
|
||||||
TOOL_ACTION PCB_ACTIONS::deleteFull( "pcbnew.InteractiveEdit.deleteFull",
|
TOOL_ACTION PCB_ACTIONS::deleteFull( "pcbnew.InteractiveEdit.deleteFull",
|
||||||
AS_GLOBAL,
|
AS_GLOBAL,
|
||||||
MD_SHIFT + WXK_DELETE, LEGACY_HK_NAME( "Delete Full Track" ),
|
MD_SHIFT + WXK_DELETE, LEGACY_HK_NAME( "Delete Full Track" ),
|
||||||
|
|
|
@ -140,6 +140,7 @@ public:
|
||||||
|
|
||||||
/// Fillet (i.e. adds an arc tangent to) all selected straight tracks by a user defined radius
|
/// Fillet (i.e. adds an arc tangent to) all selected straight tracks by a user defined radius
|
||||||
static TOOL_ACTION filletTracks;
|
static TOOL_ACTION filletTracks;
|
||||||
|
static TOOL_ACTION filletLines;
|
||||||
|
|
||||||
/// Activation of the edit tool
|
/// Activation of the edit tool
|
||||||
static TOOL_ACTION properties;
|
static TOOL_ACTION properties;
|
||||||
|
|
Loading…
Reference in New Issue