ADDED: Heal Shapes; Fix discontinuities in gfx import and Cleanup dialog.

This commit is contained in:
Alex Shvartzkop 2023-10-17 10:27:59 +03:00
parent 63ba3b8ea7
commit be1008cbd8
27 changed files with 1328 additions and 95 deletions

View File

@ -325,6 +325,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::group_leave].emplace_back( BITMAPS::group_leave, wxT( "group_leave_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::group_remove].emplace_back( BITMAPS::group_remove, wxT( "group_remove_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::group_ungroup].emplace_back( BITMAPS::group_ungroup, wxT( "group_ungroup_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::heal_shapes].emplace_back( BITMAPS::heal_shapes, wxT( "heal_shapes_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::help].emplace_back( BITMAPS::help, wxT( "help_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::help_online].emplace_back( BITMAPS::help_online, wxT( "help_online_24.png" ), 24, wxT( "light" ) );
aBitmapInfoCache[BITMAPS::hidden_pin].emplace_back( BITMAPS::hidden_pin, wxT( "hidden_pin_24.png" ), 24, wxT( "light" ) );
@ -717,6 +718,7 @@ void BuildBitmapInfo( std::unordered_map<BITMAPS, std::vector<BITMAP_INFO>>& aBi
aBitmapInfoCache[BITMAPS::group_leave].emplace_back( BITMAPS::group_leave, wxT( "group_leave_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::group_remove].emplace_back( BITMAPS::group_remove, wxT( "group_remove_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::group_ungroup].emplace_back( BITMAPS::group_ungroup, wxT( "group_ungroup_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::heal_shapes].emplace_back( BITMAPS::heal_shapes, wxT( "heal_shapes_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::help].emplace_back( BITMAPS::help, wxT( "help_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::help_online].emplace_back( BITMAPS::help_online, wxT( "help_online_dark_24.png" ), 24, wxT( "dark" ) );
aBitmapInfoCache[BITMAPS::hidden_pin].emplace_back( BITMAPS::hidden_pin, wxT( "hidden_pin_dark_24.png" ), 24, wxT( "dark" ) );

View File

@ -229,6 +229,7 @@ enum class BITMAPS : unsigned int
group_leave,
group_remove,
group_ungroup,
heal_shapes,
help,
help_online,
hidden_pin,

View File

@ -103,7 +103,7 @@ public:
*/
void AddSeparator( int aOrder = ANY_ORDER );
void AddSeparator( const SELECTION_CONDITION& aCondition, int aOrder );
void AddSeparator( const SELECTION_CONDITION& aCondition, int aOrder = ANY_ORDER );
/**
* Update the contents of the menu based on the supplied conditions.

View File

@ -593,23 +593,27 @@ SHAPE_ARC SHAPE_ARC::Reversed() const
bool SHAPE_ARC::sliceContainsPoint( const VECTOR2I& p ) const
{
VECTOR2I center = GetCenter();
EDA_ANGLE phi( p - center );
EDA_ANGLE sa = GetStartAngle().Normalize();
EDA_ANGLE ca = GetCentralAngle();
EDA_ANGLE sa = GetStartAngle();
EDA_ANGLE ea;
EDA_ANGLE ea = sa + ca;
EDA_ANGLE phi( p - GetCenter() ); // Angle from center to the point
phi.Normalize();
if( ca >= ANGLE_0 )
{
ea = sa + ca;
while( phi < sa )
phi += ANGLE_360;
return phi >= sa && phi <= ea;
}
else
{
ea = sa;
sa += ca;
}
while( phi > sa )
phi -= ANGLE_360;
return alg::within_wrapped_range( phi.AsDegrees(), sa.AsDegrees(), ea.AsDegrees(), 360.0 );
return phi <= sa && phi >= ea;
}
}

View File

@ -31,11 +31,14 @@
#include <pcb_base_frame.h>
static int s_defaultTolerance = pcbIUScale.mmToIU( 2 );
DIALOG_CLEANUP_GRAPHICS::DIALOG_CLEANUP_GRAPHICS( PCB_BASE_FRAME* aParent,
bool aIsFootprintEditor ) :
DIALOG_CLEANUP_GRAPHICS_BASE( aParent ),
m_parentFrame( aParent ),
m_isFootprintEditor( aIsFootprintEditor )
m_isFootprintEditor( aIsFootprintEditor ),
m_tolerance( aParent, m_toleranceLabel, m_toleranceCtrl, m_toleranceUnits )
{
m_changesTreeModel = new RC_TREE_MODEL( m_parentFrame, m_changesDataView );
m_changesDataView->AssociateModel( m_changesTreeModel );
@ -44,6 +47,9 @@ DIALOG_CLEANUP_GRAPHICS::DIALOG_CLEANUP_GRAPHICS( PCB_BASE_FRAME* aParent,
{
SetupStandardButtons( { { wxID_OK, _( "Update Footprint" ) } } );
m_nettieHint->SetFont( KIUI::GetInfoFont( aParent ).Italic() );
m_fixBoardOutlines->Show( false );
m_toleranceSizer->Show( false );
}
else
{
@ -71,6 +77,8 @@ void DIALOG_CLEANUP_GRAPHICS::OnCheckBox( wxCommandEvent& anEvent )
bool DIALOG_CLEANUP_GRAPHICS::TransferDataToWindow()
{
m_tolerance.SetValue( s_defaultTolerance );
doCleanup( true );
return true;
@ -79,6 +87,8 @@ bool DIALOG_CLEANUP_GRAPHICS::TransferDataToWindow()
bool DIALOG_CLEANUP_GRAPHICS::TransferDataFromWindow()
{
s_defaultTolerance = m_tolerance.GetValue();
doCleanup( false );
return true;
@ -110,8 +120,8 @@ void DIALOG_CLEANUP_GRAPHICS::doCleanup( bool aDryRun )
m_parentFrame->Compile_Ratsnest( false );
cleaner.CleanupBoard( aDryRun, &m_items, m_createRectanglesOpt->GetValue(),
m_deleteRedundantOpt->GetValue(),
m_mergePadsOpt->GetValue() );
m_deleteRedundantOpt->GetValue(), m_mergePadsOpt->GetValue(),
m_fixBoardOutlines, m_tolerance.GetIntValue() );
if( aDryRun )
{

View File

@ -27,6 +27,7 @@
#include <dialog_cleanup_graphics_base.h>
#include <cleanup_item.h>
#include <widgets/unit_binder.h>
class PCB_BASE_FRAME;
@ -52,6 +53,8 @@ private:
bool m_isFootprintEditor;
RC_TREE_MODEL* m_changesTreeModel;
UNIT_BINDER m_tolerance;
std::vector<std::shared_ptr<CLEANUP_ITEM>> m_items;
};

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -38,6 +38,25 @@ DIALOG_CLEANUP_GRAPHICS_BASE::DIALOG_CLEANUP_GRAPHICS_BASE( wxWindow* parent, wx
bSizerUpper->Add( bSizerMargins, 1, wxEXPAND|wxALL, 3 );
m_fixBoardOutlines = new wxCheckBox( this, wxID_ANY, _("Fix discontinuities in board outlines"), wxDefaultPosition, wxDefaultSize, 0 );
bSizerUpper->Add( m_fixBoardOutlines, 0, wxALL, 5 );
m_toleranceSizer = new wxBoxSizer( wxHORIZONTAL );
m_toleranceLabel = new wxStaticText( this, wxID_ANY, _("Tolerance:"), wxDefaultPosition, wxDefaultSize, 0 );
m_toleranceLabel->Wrap( -1 );
m_toleranceSizer->Add( m_toleranceLabel, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 25 );
m_toleranceCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_toleranceSizer->Add( m_toleranceCtrl, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
m_toleranceUnits = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_toleranceUnits->Wrap( -1 );
m_toleranceSizer->Add( m_toleranceUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
bSizerUpper->Add( m_toleranceSizer, 1, wxEXPAND, 5 );
bSizerMain->Add( bSizerUpper, 0, wxEXPAND|wxALL, 5 );
@ -75,6 +94,7 @@ DIALOG_CLEANUP_GRAPHICS_BASE::DIALOG_CLEANUP_GRAPHICS_BASE( wxWindow* parent, wx
m_createRectanglesOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_deleteRedundantOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_mergePadsOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_fixBoardOutlines->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_changesDataView->Connect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnSelectItem ), NULL, this );
m_changesDataView->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnLeftDClickItem ), NULL, this );
}
@ -85,6 +105,7 @@ DIALOG_CLEANUP_GRAPHICS_BASE::~DIALOG_CLEANUP_GRAPHICS_BASE()
m_createRectanglesOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_deleteRedundantOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_mergePadsOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_fixBoardOutlines->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this );
m_changesDataView->Disconnect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnSelectItem ), NULL, this );
m_changesDataView->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnLeftDClickItem ), NULL, this );

View File

@ -337,6 +337,268 @@
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Fix discontinuities in board outlines</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_fixBoardOutlines</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnCheckBox">OnCheckBox</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">m_toleranceSizer</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">protected</property>
<object class="sizeritem" expanded="1">
<property name="border">25</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Tolerance:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_toleranceLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_toleranceCtrl</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">unit</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_toleranceUnits</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -19,6 +19,7 @@
#include <wx/settings.h>
#include <wx/stattext.h>
#include <wx/sizer.h>
#include <wx/textctrl.h>
#include <wx/dataview.h>
#include <wx/button.h>
#include <wx/dialog.h>
@ -38,6 +39,11 @@ class DIALOG_CLEANUP_GRAPHICS_BASE : public DIALOG_SHIM
wxCheckBox* m_deleteRedundantOpt;
wxCheckBox* m_mergePadsOpt;
wxStaticText* m_nettieHint;
wxCheckBox* m_fixBoardOutlines;
wxBoxSizer* m_toleranceSizer;
wxStaticText* m_toleranceLabel;
wxTextCtrl* m_toleranceCtrl;
wxStaticText* m_toleranceUnits;
wxStaticText* staticChangesLabel;
wxDataViewCtrl* m_changesDataView;
wxStdDialogButtonSizer* m_sdbSizer;

View File

@ -27,8 +27,7 @@ static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const VECTOR2I& aPoint,
return graphic;
}
// Search again for anything that's close, even something already used. (The latter is
// important for error reporting.)
// Search again for anything that's close.
VECTOR2I pt( aPoint );
SEG::ecoord closest_dist_sq = SEG::Square( aLimit );
PCB_SHAPE* closest_graphic = nullptr;
@ -80,6 +79,13 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
return ( aRef - aFirst ).SquaredEuclideanNorm() < ( aRef - aSecond ).SquaredEuclideanNorm();
};
auto min_distance_sq = []( const VECTOR2I& aRef, const VECTOR2I& aFirst,
const VECTOR2I& aSecond ) -> bool
{
return std::min( ( aRef - aFirst ).SquaredEuclideanNorm(),
( aRef - aSecond ).SquaredEuclideanNorm() );
};
auto addSegment = [&]( const VECTOR2I start, const VECTOR2I end, int width, PCB_LAYER_ID layer )
{
std::unique_ptr<PCB_SHAPE> seg = std::make_unique<PCB_SHAPE>( nullptr, SHAPE_T::SEGMENT );
@ -151,6 +157,13 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
for( const VECTOR2I& ip : ips )
{
if( min_distance_sq( ip, segShape->GetStart(), segShape->GetEnd() ) <= 0
&& min_distance_sq( ip, arcShape->GetStart(), arcShape->GetEnd() ) <= 0 )
{
// Already connected
continue;
}
if( seg.Distance( ip ) <= aChainingEpsilon )
{
if( closer_to_first( ip, seg.A, seg.B ) )
@ -158,35 +171,46 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
else
segShape->SetEnd( ip );
// Move points in the actual PCB_SHAPE
if( closer_to_first( ip, arc.GetP0(), arc.GetP1() ) )
arcShape->SetArcGeometry( ip, arc.GetArcMid(), arc.GetP1() );
else
arcShape->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), ip );
// Reconstruct the arc shape - we may have more than 1 intersection
arc = SHAPE_ARC( arcShape->GetStart(), arcShape->GetArcMid(),
arcShape->GetEnd(), 0 );
success = true;
break;
}
}
if( !success )
{
// Try to avoid acute angles
VECTOR2I lineProj = seg.LineProject( arc.GetCenter() );
bool intersectsPerp = seg.SquaredDistance( lineProj ) <= 0;
if( closer_to_first( lineProj, seg.A, seg.B ) )
segShape->SetStart( lineProj );
else
segShape->SetEnd( lineProj );
if( intersectsPerp )
{
if( closer_to_first( lineProj, seg.A, seg.B ) )
segShape->SetStart( lineProj );
else
segShape->SetEnd( lineProj );
CIRCLE circ( arc.GetCenter(), arc.GetRadius() );
VECTOR2I circProj = circ.NearestPoint( lineProj );
CIRCLE circ( arc.GetCenter(), arc.GetRadius() );
VECTOR2I circProj = circ.NearestPoint( lineProj );
if( closer_to_first( circProj, arc.GetP0(), arc.GetP1() ) )
arcShape->SetArcGeometry( circProj, arc.GetArcMid(), arc.GetP1() );
else
arcShape->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), circProj );
if( closer_to_first( circProj, arc.GetP0(), arc.GetP1() ) )
arcShape->SetArcGeometry( circProj, arc.GetArcMid(), arc.GetP1() );
else
arcShape->SetArcGeometry( arc.GetP0(), arc.GetArcMid(), circProj );
addSegment( circProj, lineProj, segShape->GetWidth(), segShape->GetLayer() );
success = true;
addSegment( circProj, lineProj, segShape->GetWidth(), segShape->GetLayer() );
success = true;
}
}
}
@ -217,7 +241,8 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
for( ;; )
{
// Get next closest segment.
PCB_SHAPE* nextGraphic = findNext( curr_graphic, prevPt, aShapeList, aChainingEpsilon );
PCB_SHAPE* nextGraphic =
findNext( curr_graphic, prevPt, aShapeList, aChainingEpsilon );
if( !nextGraphic )
break;
@ -229,7 +254,8 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
std::swap( nstart, nend );
if( !connectPair( curr_graphic, nextGraphic ) )
addSegment( prevPt, nstart, curr_graphic->GetWidth(), curr_graphic->GetLayer() );
addSegment( prevPt, nstart, curr_graphic->GetWidth(),
curr_graphic->GetLayer() );
// Shape might've changed
nstart = nextGraphic->GetStart();
@ -245,8 +271,41 @@ void ConnectBoardShapes( std::vector<PCB_SHAPE*>& aShapeList,
}
};
walkFrom( graphic, graphic->GetEnd() );
walkFrom( graphic, graphic->GetStart() );
const VECTOR2I ptEnd = graphic->GetEnd();
const VECTOR2I ptStart = graphic->GetStart();
PCB_SHAPE* grAtEnd = findNext( graphic, ptEnd, aShapeList, aChainingEpsilon );
PCB_SHAPE* grAtStart = findNext( graphic, ptStart, aShapeList, aChainingEpsilon );
bool beginFromEndPt = true;
// We need to start walking from a point that is closest to a point of another shape.
if( grAtEnd && grAtStart )
{
SEG::ecoord dAtEnd = min_distance_sq( ptEnd, grAtEnd->GetStart(), grAtEnd->GetEnd() );
SEG::ecoord dAtStart =
min_distance_sq( ptStart, grAtStart->GetStart(), grAtStart->GetEnd() );
beginFromEndPt = dAtEnd <= dAtStart;
}
else if( grAtEnd )
beginFromEndPt = true;
else if( grAtStart )
beginFromEndPt = false;
if( beginFromEndPt )
{
// Do not inline GetEnd / GetStart as endpoints may update
walkFrom( graphic, graphic->GetEnd() );
walkFrom( graphic, graphic->GetStart() );
}
else
{
walkFrom( graphic, graphic->GetStart() );
walkFrom( graphic, graphic->GetEnd() );
}
startCandidates.erase( graphic );
}
}

View File

@ -31,6 +31,7 @@
#include <pad.h>
#include <footprint.h>
#include <graphics_cleaner.h>
#include <fix_board_shape.h>
#include <board_design_settings.h>
#include <tool/tool_manager.h>
#include <tools/pad_tool.h>
@ -48,12 +49,14 @@ GRAPHICS_CLEANER::GRAPHICS_CLEANER( DRAWINGS& aDrawings, FOOTPRINT* aParentFootp
}
void GRAPHICS_CLEANER::CleanupBoard( bool aDryRun,
void GRAPHICS_CLEANER::CleanupBoard( bool aDryRun,
std::vector<std::shared_ptr<CLEANUP_ITEM>>* aItemsList,
bool aMergeRects, bool aDeleteRedundant, bool aMergePads )
bool aMergeRects, bool aDeleteRedundant, bool aMergePads,
bool aFixBoardOutlines, int aTolerance )
{
m_dryRun = aDryRun;
m_itemsList = aItemsList;
m_outlinesTolerance = aTolerance;
m_epsilon = m_commit.GetBoard()->GetDesignSettings().m_MaxError;
@ -64,6 +67,9 @@ void GRAPHICS_CLEANER::CleanupBoard( bool aDryRun,
if( aDeleteRedundant )
cleanupShapes();
if( aFixBoardOutlines )
fixBoardOutlines();
if( aMergeRects )
mergeRects();
@ -199,6 +205,36 @@ void GRAPHICS_CLEANER::cleanupShapes()
}
void GRAPHICS_CLEANER::fixBoardOutlines()
{
if( m_dryRun )
return;
std::vector<PCB_SHAPE*> shapeList;
std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
for( BOARD_ITEM* item : m_drawings )
{
PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item );
if( !shape || !shape->IsOnLayer( Edge_Cuts ) )
continue;
shapeList.push_back( shape );
if( !m_dryRun )
m_commit.Modify( shape );
}
ConnectBoardShapes( shapeList, newShapes, m_outlinesTolerance );
std::vector<PCB_SHAPE*> items_to_select;
for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
m_commit.Add( ptr.release() );
}
void GRAPHICS_CLEANER::mergeRects()
{
struct SIDE_CANDIDATE

View File

@ -46,14 +46,16 @@ public:
* @param aMergePads = true to apply Pad Editor's merge algorithm to all pads in footprint
* (it is assumed this will only be run on FPEditor boards)
*/
void CleanupBoard( bool aDryRun, std::vector<std::shared_ptr<CLEANUP_ITEM> >* aItemsList,
bool aMergeRects, bool aDeleteRedundant, bool aMergePads );
void CleanupBoard( bool aDryRun, std::vector<std::shared_ptr<CLEANUP_ITEM>>* aItemsList,
bool aMergeRects, bool aDeleteRedundant, bool aMergePads,
bool aFixBoardOutlines, int aTolerance );
private:
bool isNullShape( PCB_SHAPE* aShape );
bool areEquivalent( PCB_SHAPE* aShape1, PCB_SHAPE* aShape2 );
void cleanupShapes();
void fixBoardOutlines();
void mergeRects();
void mergePads();
@ -64,6 +66,7 @@ private:
TOOL_MANAGER* m_toolMgr;
bool m_dryRun;
int m_epsilon;
int m_outlinesTolerance;
std::vector<std::shared_ptr<CLEANUP_ITEM>>* m_itemsList;
};

View File

@ -40,9 +40,11 @@
#include <memory>
// Static members of DIALOG_IMPORT_GFX_PCB, to remember the user's choices during the session
bool DIALOG_IMPORT_GFX_PCB::m_placementInteractive = true;
bool DIALOG_IMPORT_GFX_PCB::m_shouldGroupItems = true;
double DIALOG_IMPORT_GFX_PCB::m_importScale = 1.0; // Do not change the imported items size
bool DIALOG_IMPORT_GFX_PCB::s_placementInteractive = true;
bool DIALOG_IMPORT_GFX_PCB::s_shouldGroupItems = true;
bool DIALOG_IMPORT_GFX_PCB::s_fixDiscontinuities = true;
int DIALOG_IMPORT_GFX_PCB::s_toleranceValue = pcbIUScale.mmToIU( 1 );
double DIALOG_IMPORT_GFX_PCB::s_importScale = 1.0; // Do not change the imported items size
const std::map<DXF_IMPORT_UNITS, wxString> dxfUnitsMap = {
@ -59,7 +61,8 @@ DIALOG_IMPORT_GFX_PCB::DIALOG_IMPORT_GFX_PCB( PCB_BASE_FRAME* aParent, bool aImp
m_parent( aParent ),
m_xOrigin( aParent, m_xLabel, m_xCtrl, m_xUnits ),
m_yOrigin( aParent, m_yLabel, m_yCtrl, m_yUnits ),
m_defaultLineWidth( aParent, m_lineWidthLabel, m_lineWidthCtrl, m_lineWidthUnits )
m_defaultLineWidth( aParent, m_lineWidthLabel, m_lineWidthCtrl, m_lineWidthUnits ),
m_tolerance( aParent, m_toleranceLabel, m_toleranceCtrl, m_toleranceUnits )
{
if( aImportAsFootprintGraphic )
m_importer = std::make_unique<GRAPHICS_IMPORTER_FOOTPRINT>( m_parent->GetBoard()->GetFirstFootprint() );
@ -84,18 +87,21 @@ DIALOG_IMPORT_GFX_PCB::DIALOG_IMPORT_GFX_PCB( PCB_BASE_FRAME* aParent, bool aImp
PCBNEW_SETTINGS* cfg = m_parent->GetPcbNewSettings();
m_placementInteractive = cfg->m_ImportGraphics.interactive_placement;
s_placementInteractive = cfg->m_ImportGraphics.interactive_placement;
m_xOrigin.SetValue( cfg->m_ImportGraphics.origin_x * pcbIUScale.IU_PER_MM );
m_yOrigin.SetValue( cfg->m_ImportGraphics.origin_y * pcbIUScale.IU_PER_MM );
m_defaultLineWidth.SetValue( cfg->m_ImportGraphics.dxf_line_width * pcbIUScale.IU_PER_MM );
m_textCtrlFileName->SetValue( cfg->m_ImportGraphics.last_file );
m_rbInteractivePlacement->SetValue( m_placementInteractive );
m_rbAbsolutePlacement->SetValue( !m_placementInteractive );
m_groupItems->SetValue( m_shouldGroupItems );
m_importScaleCtrl->SetValue( wxString::Format( wxT( "%f" ), s_importScale ) );
m_importScaleCtrl->SetValue( wxString::Format( wxT( "%f" ), m_importScale ) );
m_textCtrlFileName->SetValue( cfg->m_ImportGraphics.last_file );
m_rbInteractivePlacement->SetValue( s_placementInteractive );
m_rbAbsolutePlacement->SetValue( !s_placementInteractive );
m_groupItems->SetValue( s_shouldGroupItems );
m_tolerance.SetValue( s_toleranceValue );
m_rbFixDiscontinuities->SetValue( s_fixDiscontinuities );
// Configure the layers list selector
m_SelLayerBox->SetLayersHotkeys( false ); // Do not display hotkeys
@ -141,19 +147,19 @@ DIALOG_IMPORT_GFX_PCB::~DIALOG_IMPORT_GFX_PCB()
wxFAIL_MSG( e.what() );
}
if( cfg )
if( cfg )
{
cfg->m_ImportGraphics.layer = m_SelLayerBox->GetLayerSelection();
cfg->m_ImportGraphics.interactive_placement = m_placementInteractive;
cfg->m_ImportGraphics.last_file = m_textCtrlFileName->GetValue();
cfg->m_ImportGraphics.dxf_line_width =
pcbIUScale.IUTomm( m_defaultLineWidth.GetValue() );
cfg->m_ImportGraphics.origin_x = pcbIUScale.IUTomm( m_xOrigin.GetValue() );
cfg->m_ImportGraphics.origin_y = pcbIUScale.IUTomm( m_yOrigin.GetValue() );
cfg->m_ImportGraphics.dxf_units = m_choiceDxfUnits->GetSelection();
cfg->m_ImportGraphics.layer = m_SelLayerBox->GetLayerSelection();
cfg->m_ImportGraphics.interactive_placement = s_placementInteractive;
cfg->m_ImportGraphics.last_file = m_textCtrlFileName->GetValue();
cfg->m_ImportGraphics.dxf_line_width = pcbIUScale.IUTomm( m_defaultLineWidth.GetValue() );
cfg->m_ImportGraphics.origin_x = pcbIUScale.IUTomm( m_xOrigin.GetValue() );
cfg->m_ImportGraphics.origin_y = pcbIUScale.IUTomm( m_yOrigin.GetValue() );
cfg->m_ImportGraphics.dxf_units = m_choiceDxfUnits->GetSelection();
}
m_importScale = EDA_UNIT_UTILS::UI::DoubleValueFromString( m_importScaleCtrl->GetValue() );
s_importScale = EDA_UNIT_UTILS::UI::DoubleValueFromString( m_importScaleCtrl->GetValue() );
s_toleranceValue = m_tolerance.GetIntValue();
m_textCtrlFileName->Disconnect( wxEVT_COMMAND_TEXT_UPDATED,
wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB::onFilename ),
@ -299,14 +305,16 @@ bool DIALOG_IMPORT_GFX_PCB::TransferDataFromWindow()
void DIALOG_IMPORT_GFX_PCB::originOptionOnUpdateUI( wxUpdateUIEvent& event )
{
if( m_rbInteractivePlacement->GetValue() != m_placementInteractive )
m_rbInteractivePlacement->SetValue( m_placementInteractive );
if( m_rbInteractivePlacement->GetValue() != s_placementInteractive )
m_rbInteractivePlacement->SetValue( s_placementInteractive );
if( m_rbAbsolutePlacement->GetValue() == m_placementInteractive )
m_rbAbsolutePlacement->SetValue( !m_placementInteractive );
if( m_rbAbsolutePlacement->GetValue() == s_placementInteractive )
m_rbAbsolutePlacement->SetValue( !s_placementInteractive );
m_xOrigin.Enable( !m_placementInteractive );
m_yOrigin.Enable( !m_placementInteractive );
m_xOrigin.Enable( !s_placementInteractive );
m_yOrigin.Enable( !s_placementInteractive );
m_tolerance.Enable( s_fixDiscontinuities );
}

View File

@ -53,12 +53,22 @@ public:
* items must be moved by the mouse cursor to the final position
* false means the imported items are placed to the final position after import.
*/
bool IsPlacementInteractive() { return m_placementInteractive; }
bool IsPlacementInteractive() { return s_placementInteractive; }
/**
* @return true if the items should be added into a group when being placed.
*/
bool ShouldGroupItems() { return m_shouldGroupItems; }
bool ShouldGroupItems() { return s_shouldGroupItems; }
/**
* @return true if discontinuities in the shapes should be fixed.
*/
bool ShouldFixDiscontinuities() { return s_fixDiscontinuities; }
/**
* @return tolerance to connect the shapes.
*/
int GetTolerance() { return s_toleranceValue; }
bool TransferDataFromWindow() override;
@ -70,17 +80,23 @@ private:
void onInteractivePlacement( wxCommandEvent& event ) override
{
m_placementInteractive = true;
s_placementInteractive = true;
}
void onAbsolutePlacement( wxCommandEvent& event ) override
{
m_placementInteractive = false;
s_placementInteractive = false;
}
void onGroupItems( wxCommandEvent& event ) override
{
m_shouldGroupItems = m_groupItems->GetValue();
s_shouldGroupItems = m_groupItems->GetValue();
}
void onFixDiscontinuities( wxCommandEvent& event ) override
{
s_fixDiscontinuities = m_rbFixDiscontinuities->GetValue();
m_tolerance.Enable( s_fixDiscontinuities );
}
private:
@ -91,10 +107,13 @@ private:
UNIT_BINDER m_xOrigin;
UNIT_BINDER m_yOrigin;
UNIT_BINDER m_defaultLineWidth;
UNIT_BINDER m_tolerance;
static bool m_shouldGroupItems;
static bool m_placementInteractive;
static double m_importScale; // a scale factor to change the size of imported
static bool s_shouldGroupItems;
static bool s_placementInteractive;
static bool s_fixDiscontinuities;
static int s_toleranceValue;
static double s_importScale; // a scale factor to change the size of imported
// items m_importScale =1.0 means keep original size
};

View File

@ -143,10 +143,10 @@ DIALOG_IMPORT_GFX_PCB_BASE::DIALOG_IMPORT_GFX_PCB_BASE( wxWindow* parent, wxWind
fgSizerImportSettings->Add( m_importScaleCtrl, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
fgSizerImportSettings->Add( 0, 0, 0, 0, 5 );
fgSizerImportSettings->Add( 0, 0, 1, wxEXPAND, 5 );
bSizer7->Add( fgSizerImportSettings, 1, wxEXPAND|wxBOTTOM|wxRIGHT, 5 );
bSizer7->Add( fgSizerImportSettings, 1, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
sbSizer1->Add( bSizer7, 1, wxEXPAND, 5 );
@ -154,16 +154,47 @@ DIALOG_IMPORT_GFX_PCB_BASE::DIALOG_IMPORT_GFX_PCB_BASE( wxWindow* parent, wxWind
m_staticline1 = new wxStaticLine( sbSizer1->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
sbSizer1->Add( m_staticline1, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
wxBoxSizer* bSizer71;
bSizer71 = new wxBoxSizer( wxVERTICAL );
m_rbFixDiscontinuities = new wxCheckBox( sbSizer1->GetStaticBox(), wxID_ANY, _("Fix discontinuities"), wxDefaultPosition, wxDefaultSize, 0 );
m_rbFixDiscontinuities->SetToolTip( _("Trim/extend open shapes or add segments to make vertices of shapes coincide") );
bSizer71->Add( m_rbFixDiscontinuities, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
wxBoxSizer* bSizer14;
bSizer14 = new wxBoxSizer( wxHORIZONTAL );
m_toleranceLabel = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("Tolerance:"), wxDefaultPosition, wxDefaultSize, 0 );
m_toleranceLabel->Wrap( -1 );
bSizer14->Add( m_toleranceLabel, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 );
m_toleranceCtrl = new wxTextCtrl( sbSizer1->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
bSizer14->Add( m_toleranceCtrl, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_toleranceUnits = new wxStaticText( sbSizer1->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_toleranceUnits->Wrap( -1 );
bSizer14->Add( m_toleranceUnits, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
bSizer71->Add( bSizer14, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
sbSizer1->Add( bSizer71, 0, wxEXPAND, 5 );
m_staticline11 = new wxStaticLine( sbSizer1->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
sbSizer1->Add( m_staticline11, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
wxBoxSizer* bSizer8;
bSizer8 = new wxBoxSizer( wxVERTICAL );
m_groupItems = new wxCheckBox( sbSizer1->GetStaticBox(), wxID_ANY, _("Group items"), wxDefaultPosition, wxDefaultSize, 0 );
m_groupItems->SetToolTip( _("Add all imported items into a new group") );
bSizer8->Add( m_groupItems, 0, wxEXPAND|wxBOTTOM, 5 );
bSizer8->Add( m_groupItems, 0, wxEXPAND|wxBOTTOM|wxLEFT, 5 );
sbSizer1->Add( bSizer8, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 );
sbSizer1->Add( bSizer8, 0, wxEXPAND|wxTOP, 5 );
bSizerMain->Add( sbSizer1, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 10 );
@ -234,6 +265,7 @@ DIALOG_IMPORT_GFX_PCB_BASE::DIALOG_IMPORT_GFX_PCB_BASE( wxWindow* parent, wxWind
m_rbInteractivePlacement->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::originOptionOnUpdateUI ), NULL, this );
m_rbAbsolutePlacement->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::onAbsolutePlacement ), NULL, this );
m_rbAbsolutePlacement->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::originOptionOnUpdateUI ), NULL, this );
m_rbFixDiscontinuities->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::onFixDiscontinuities ), NULL, this );
m_groupItems->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::onGroupItems ), NULL, this );
}
@ -245,6 +277,7 @@ DIALOG_IMPORT_GFX_PCB_BASE::~DIALOG_IMPORT_GFX_PCB_BASE()
m_rbInteractivePlacement->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::originOptionOnUpdateUI ), NULL, this );
m_rbAbsolutePlacement->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::onAbsolutePlacement ), NULL, this );
m_rbAbsolutePlacement->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::originOptionOnUpdateUI ), NULL, this );
m_rbFixDiscontinuities->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::onFixDiscontinuities ), NULL, this );
m_groupItems->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_IMPORT_GFX_PCB_BASE::onGroupItems ), NULL, this );
}

View File

@ -845,7 +845,7 @@
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxBOTTOM|wxRIGHT</property>
<property name="flag">wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT</property>
<property name="proportion">1</property>
<object class="wxFlexGridSizer" expanded="1">
<property name="cols">3</property>
@ -1120,11 +1120,11 @@
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="0">
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag"></property>
<property name="proportion">0</property>
<object class="spacer" expanded="0">
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="1">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
@ -1192,10 +1192,341 @@
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer71</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxBOTTOM|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Fix discontinuities</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_rbFixDiscontinuities</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Trim/extend open shapes or add segments to make vertices of shapes coincide</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnCheckBox">onFixDiscontinuities</event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP|wxBOTTOM</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer14</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Tolerance:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_toleranceLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
<property name="proportion">1</property>
<object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_toleranceCtrl</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">mm</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_toleranceUnits</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP|wxBOTTOM</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticline11</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxLI_HORIZONTAL</property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxTOP</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizer8</property>
@ -1203,7 +1534,7 @@
<property name="permission">none</property>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxBOTTOM</property>
<property name="flag">wxEXPAND|wxBOTTOM|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="0">
<property name="BottomDockable">1</property>

View File

@ -63,6 +63,11 @@ class DIALOG_IMPORT_GFX_PCB_BASE : public DIALOG_SHIM
wxStaticText* m_importScaleLabel;
wxTextCtrl* m_importScaleCtrl;
wxStaticLine* m_staticline1;
wxCheckBox* m_rbFixDiscontinuities;
wxStaticText* m_toleranceLabel;
wxTextCtrl* m_toleranceCtrl;
wxStaticText* m_toleranceUnits;
wxStaticLine* m_staticline11;
wxCheckBox* m_groupItems;
wxStaticText* m_lineWidthLabel;
wxTextCtrl* m_lineWidthCtrl;
@ -78,6 +83,7 @@ class DIALOG_IMPORT_GFX_PCB_BASE : public DIALOG_SHIM
virtual void onInteractivePlacement( wxCommandEvent& event ) { event.Skip(); }
virtual void originOptionOnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void onAbsolutePlacement( wxCommandEvent& event ) { event.Skip(); }
virtual void onFixDiscontinuities( wxCommandEvent& event ) { event.Skip(); }
virtual void onGroupItems( wxCommandEvent& event ) { event.Skip(); }

View File

@ -72,6 +72,7 @@
#include <scoped_set_reset.h>
#include <string_utils.h>
#include <zone.h>
#include <fix_board_shape.h>
const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
@ -1522,6 +1523,26 @@ int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
preview.Add( group );
}
if( dlg.ShouldFixDiscontinuities() )
{
std::vector<PCB_SHAPE*> shapeList;
std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
for( const std::unique_ptr<EDA_ITEM>& ptr : list )
{
if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
shapeList.push_back( shape );
}
ConnectBoardShapes( shapeList, newShapes, dlg.GetTolerance() );
for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
{
ptr->SetParent( m_frame->GetBoard() );
list.push_back( std::move( ptr ) );
}
}
for( std::unique_ptr<EDA_ITEM>& ptr : list )
{
BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( ptr.get() );
@ -2971,7 +2992,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
int menuID = 1;
for( int netcode : aNetcodeList )
{
{
wxString menuText;
if( menuID < 10 )
{
@ -2999,7 +3020,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
drawingTool->SetContextMenu( &menu, CMENU_NOW );
int selectNetCode = -1;
while( TOOL_EVENT* evt = drawingTool->Wait() )
{
@ -3010,7 +3031,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
{
std::optional<int> id = evt->GetCommandId();
// User has selected an item, so this one will be returned
if( id && ( *id > 0 ) && ( *id < menuID ) )
{
@ -3039,7 +3060,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
const LSET lset = aVia->GetLayerSet();
PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
bool highContrast = ( opts.m_ContrastModeDisplay != HIGH_CONTRAST_MODE::NORMAL );
// In high contrast mode, it should be treated as the only one visible layer
// We just return the net of active layer (not the visible layers) without show menu
if( highContrast )
@ -3060,7 +3081,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
{
LSET tempLset = LSET( m_board->GetVisibleLayers() & lset );
std::list<int> netcodeList;
// When there is only one visible layer and the others invisible,
// we find net in the only visible layer instead of showing menu
if( 1 != tempLset.Seq().size() )
@ -3079,10 +3100,10 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
if( z->HitTestFilledArea( layer, position ) )
netcodeList.push_back( z->GetNetCode() );
}
}
}
netcodeList.sort();
netcodeList.unique();
@ -3090,7 +3111,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
{
return netcodeList.front();
}
// When there are more than one possible net , it's ambiguous
// So show a pop-up menu of possible nets
if( netcodeList.size() > 1 )
@ -3099,7 +3120,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
}
}
return -1;
return -1;
}
void SnapItem( BOARD_ITEM *aItem ) override
@ -3154,12 +3175,12 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
else
{
int netcode = findStitchedZoneNet( via );
// -2 signifies that the user has canceled the placement
// of the via while remaining in the via tool
if( -2 == netcode )
return false;
via->SetNetCode( netcode );
via->SetIsFree( via->GetNetCode() > 0 );
}

View File

@ -57,6 +57,7 @@
#include <connectivity/connectivity_algo.h>
#include <connectivity/connectivity_items.h>
#include <core/kicad_algo.h>
#include <fix_board_shape.h>
#include <bitmaps.h>
#include <cassert>
#include <functional>
@ -121,6 +122,10 @@ static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERAC
PCB_SHAPE_LOCATE_RECT_T,
PCB_SHAPE_LOCATE_SEGMENT_T };
static const std::vector<KICAD_T> healShapesTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
PCB_SHAPE_LOCATE_ARC_T,
PCB_SHAPE_LOCATE_BEZIER_T };
static const std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
static const std::vector<KICAD_T> polygonBooleanTypes = { PCB_SHAPE_LOCATE_RECT_T,
@ -143,6 +148,7 @@ static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERAC
};
// clang-format off
menu->AddItem( PCB_ACTIONS::healShapes, SELECTION_CONDITIONS::HasTypes( healShapesTypes ) );
menu->AddItem( PCB_ACTIONS::filletLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
menu->AddItem( PCB_ACTIONS::chamferLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
menu->AddItem( PCB_ACTIONS::extendLines, SELECTION_CONDITIONS::OnlyTypes( lineExtendTypes )
@ -150,7 +156,8 @@ static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERAC
menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
menu->AddSeparator();
menu->AddSeparator( SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
&& SELECTION_CONDITIONS::MoreThan( 1 ) );
menu->AddItem( PCB_ACTIONS::mergePolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
&& SELECTION_CONDITIONS::MoreThan( 1 ) );
@ -1407,6 +1414,85 @@ int EDIT_TOOL::ModifyLines( const TOOL_EVENT& aEvent )
}
int EDIT_TOOL::HealShapes( const TOOL_EVENT& aEvent )
{
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_ARC_T,
PCB_SHAPE_LOCATE_BEZIER_T } ) )
{
aCollector.Remove( item );
}
}
},
true /* prompt user regarding locked items */ );
// Store last used value
static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Heal Shapes" ), _( "Tolerance value:" ),
s_toleranceValue );
if( dlg.ShowModal() == wxID_CANCEL )
return 0;
s_toleranceValue = dlg.GetValue();
if( s_toleranceValue <= 0 )
return 0;
BOARD_COMMIT commit{ this };
std::vector<PCB_SHAPE*> shapeList;
std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
for( EDA_ITEM* item : selection )
{
if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
{
shapeList.push_back( shape );
commit.Modify( shape );
}
}
ConnectBoardShapes( shapeList, newShapes, s_toleranceValue );
std::vector<PCB_SHAPE*> items_to_select;
for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
{
PCB_SHAPE* shape = ptr.release();
commit.Add( shape );
items_to_select.push_back( shape );
}
commit.Push( _( "Heal shapes" ) );
// Select added items
for( PCB_SHAPE* item : items_to_select )
m_selectionTool->AddItemToSel( item, true );
if( items_to_select.size() > 0 )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
// Notify other tools of the changes
m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
return 0;
}
int EDIT_TOOL::BooleanPolygons( const TOOL_EVENT& aEvent )
{
PCB_SELECTION& selection = m_selectionTool->RequestSelection(
@ -2776,6 +2862,7 @@ void EDIT_TOOL::setTransitions()
Go( &EDIT_TOOL::FilletTracks, PCB_ACTIONS::filletTracks.MakeEvent() );
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::filletLines.MakeEvent() );
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::chamferLines.MakeEvent() );
Go( &EDIT_TOOL::HealShapes, PCB_ACTIONS::healShapes.MakeEvent() );
Go( &EDIT_TOOL::ModifyLines, PCB_ACTIONS::extendLines.MakeEvent() );
Go( &EDIT_TOOL::BooleanPolygons, PCB_ACTIONS::mergePolygons.MakeEvent() );
Go( &EDIT_TOOL::BooleanPolygons, PCB_ACTIONS::subtractPolygons.MakeEvent() );

View File

@ -126,6 +126,11 @@ public:
*/
int ModifyLines( const TOOL_EVENT& aEvent );
/**
* Make ends of selected shapes meet by extending or cutting them, or adding extra geometry.
*/
int HealShapes( const TOOL_EVENT& aEvent );
/**
* Modify selected polygons into a single polygon using boolean operations
* such as merge (union) or subtract (difference)

View File

@ -617,6 +617,13 @@ TOOL_ACTION PCB_ACTIONS::chamferLines( TOOL_ACTION_ARGS()
.Tooltip( _( "Cut away corners between selected lines" ) )
.Icon( BITMAPS::chamfer ) );
TOOL_ACTION PCB_ACTIONS::healShapes( TOOL_ACTION_ARGS()
.Name( "pcbnew.InteractiveEdit.healShapes" )
.Scope( AS_GLOBAL )
.MenuText( _( "Heal Shapes" ) )
.Tooltip( _( "Connect shapes, possibly extending or cutting them, or adding extra geometry" ) )
.Icon( BITMAPS::heal_shapes ) );
TOOL_ACTION PCB_ACTIONS::extendLines( TOOL_ACTION_ARGS()
.Name( "pcbnew.InteractiveEdit.extendLines" )
.Scope( AS_GLOBAL )
@ -1485,8 +1492,8 @@ TOOL_ACTION* PCB_ACTIONS::LayerIDToAction( PCB_LAYER_ID aLayer )
// in static action constructors
TOOL_ACTION_GROUP PCB_ACTIONS::layerDirectSwitchActions()
{
static TOOL_ACTION_GROUP group( "pcbnew.Control.DirectLayerActions" );
return group;
static TOOL_ACTION_GROUP s_toolActionGroup( "pcbnew.Control.DirectLayerActions" );
return s_toolActionGroup;
}
TOOL_ACTION PCB_ACTIONS::layerTop( TOOL_ACTION_ARGS()

View File

@ -158,6 +158,8 @@ public:
static TOOL_ACTION filletLines;
/// Chamfer (i.e. adds a straight line) all selected straight lines by a user defined setback
static TOOL_ACTION chamferLines;
/// Connect selected shapes, possibly extending or cutting them, or adding extra geometry
static TOOL_ACTION healShapes;
/// Extend selected lines to meet at a point
static TOOL_ACTION extendLines;

View File

@ -301,6 +301,7 @@ set( BMAPS_MID
group_leave
group_remove
group_ungroup
heal_shapes
help
help_online
hidden_pin

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="24"
width="24"
version="1.1"
id="svg2"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="heal_shapes.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata20">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs18" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="958"
inkscape:window-height="1000"
id="namedview16"
showgrid="true"
inkscape:zoom="20.385138"
inkscape:cx="17.022205"
inkscape:cy="10.32615"
inkscape:window-x="953"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg2"
inkscape:document-rotation="0"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
type="xygrid"
id="grid2997"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5"
spacingy="0.5" />
</sodipodi:namedview>
<path
style="fill:none;fill-opacity:1;stroke:#42b8eb;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 14.197142,19.266512 2.002678,0.450909"
id="path1016-7-9-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#8f8f8f;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 8.5260155,1.5 c 0,0 -3.1040621,20.630306 -3.3121863,20.560932"
id="path2999-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#8f8f8f;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.5,16.5 4.5,1"
id="path2999-3-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#42b8eb;stroke-width:2;stroke-linecap:round;stroke-opacity:1;paint-order:normal;stop-color:#000000"
id="path446-3-5"
sodipodi:type="arc"
sodipodi:cx="15.722403"
sodipodi:cy="-11.866173"
sodipodi:rx="12.999999"
sodipodi:ry="12.999999"
sodipodi:start="3.3015744"
sodipodi:end="3.5012912"
sodipodi:arc-type="arc"
d="M 2.888411,-13.937075 A 12.999999,12.999999 0 0 1 3.5543648,-16.44207"
transform="rotate(103.08047)"
sodipodi:open="true" />
<path
style="fill:none;fill-opacity:1;stroke:#ded3dd;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 8,5 6,17.5"
id="path1016-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#ded3dd;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 6,17.5 5.270844,1.114027"
id="path1016-7-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ded3dd;stroke-width:2;stroke-linecap:round;stroke-opacity:1;stop-color:#000000"
id="path446-3"
sodipodi:type="arc"
sodipodi:cx="15.722403"
sodipodi:cy="-11.866173"
sodipodi:rx="12.999999"
sodipodi:ry="12.999999"
sodipodi:start="3.7495252"
sodipodi:end="4.6872122"
sodipodi:open="true"
sodipodi:arc-type="arc"
d="m 5.051605,-19.291404 a 12.999999,12.999999 0 0 1 10.343534,-5.570648"
transform="rotate(103.08047)" />
<path
class="cls-4"
d="m 24,20.5 a 3,3 0 1 1 -3,-3 3,3 0 0 1 3,3 z"
id="path27965"
style="fill:#f2647e" />
<path
class="cls-4"
d="m 11.034687,4.7918758 a 3,3 0 1 1 -2.9999996,-3 3,3 0 0 1 2.9999996,3 z"
id="path27965-1"
style="fill:#f2647e" />
<path
class="cls-4"
d="m 9,17.5 a 3,3 0 1 1 -3,-3 3,3 0 0 1 3,3 z"
id="path27965-6"
style="fill:#f2647e" />
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="24"
width="24"
version="1.1"
id="svg2"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
sodipodi:docname="heal_shapes.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata20">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs18" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1009"
id="namedview16"
showgrid="true"
inkscape:zoom="20.385138"
inkscape:cx="5.3960881"
inkscape:cy="15.305268"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
inkscape:document-rotation="0"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1">
<inkscape:grid
type="xygrid"
id="grid2997"
empspacing="2"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
spacingx="0.5"
spacingy="0.5" />
</sodipodi:namedview>
<path
style="fill:none;fill-opacity:1;stroke:#1a81c4;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 14.197142,19.266512 2.002678,0.450909"
id="path1016-7-9-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#b9b9b9;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 8.5260155,1.5 c 0,0 -3.1040621,20.630306 -3.3121863,20.560932"
id="path2999-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#b9b9b9;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.5,16.5 4.5,1"
id="path2999-3-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;fill-opacity:1;stroke:#1a81c4;stroke-width:2;stroke-linecap:round;stroke-opacity:1;paint-order:normal;stop-color:#000000"
id="path446-3-5"
sodipodi:type="arc"
sodipodi:cx="15.722403"
sodipodi:cy="-11.866173"
sodipodi:rx="12.999999"
sodipodi:ry="12.999999"
sodipodi:start="3.3015744"
sodipodi:end="3.5012912"
sodipodi:arc-type="arc"
d="M 2.888411,-13.937075 A 12.999999,12.999999 0 0 1 3.5543648,-16.44207"
transform="rotate(103.08047)"
sodipodi:open="true" />
<path
style="fill:#cc0000;fill-opacity:1;stroke:#545454;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 8,5 6,17.5"
id="path1016-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:#cc0000;fill-opacity:1;stroke:#545454;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 6,17.5 5.270844,1.114027"
id="path1016-7-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#545454;stroke-width:2;stroke-linecap:round;stroke-opacity:1;stop-color:#000000"
id="path446-3"
sodipodi:type="arc"
sodipodi:cx="15.722403"
sodipodi:cy="-11.866173"
sodipodi:rx="12.999999"
sodipodi:ry="12.999999"
sodipodi:start="3.7495252"
sodipodi:end="4.6872122"
sodipodi:open="true"
sodipodi:arc-type="arc"
d="m 5.051605,-19.291404 a 12.999999,12.999999 0 0 1 10.343534,-5.570648"
transform="rotate(103.08047)" />
<path
class="cls-4"
d="m 24,20.5 a 3,3 0 1 1 -3,-3 3,3 0 0 1 3,3 z"
id="path27965"
style="fill:#bf2641" />
<path
class="cls-4"
d="m 11.034687,4.7918758 a 3,3 0 1 1 -2.9999996,-3 3,3 0 0 1 2.9999996,3 z"
id="path27965-1"
style="fill:#bf2641" />
<path
class="cls-4"
d="m 9,17.5 a 3,3 0 1 1 -3,-3 3,3 0 0 1 3,3 z"
id="path27965-6"
style="fill:#bf2641" />
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB