diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 3bbdd87235..5aac0307f7 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -56,8 +56,8 @@ set( PCBNEW_DIALOGS dialogs/dialog_block_options_base.cpp dialogs/dialog_board_setup.cpp dialogs/dialog_choose_footprint.cpp - dialogs/dialog_cleaning_options.cpp - dialogs/dialog_cleaning_options_base.cpp + dialogs/dialog_cleanup_tracks_and_vias.cpp + dialogs/dialog_cleanup_tracks_and_vias_base.cpp dialogs/dialog_copper_zones.cpp dialogs/dialog_copper_zones_base.cpp dialogs/dialog_create_array.cpp diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index 7f57511a2d..19323856bd 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -45,6 +45,13 @@ class D_PAD; class MSG_PANEL_ITEM; class SHAPE_POLY_SET; + +// Flag used in locate routines (from which endpoint work) +enum ENDPOINT_T { + ENDPOINT_START = 0, + ENDPOINT_END = 1 +}; + // Via types // Note that this enum must be synchronized to GAL_LAYER_ID enum VIATYPE_T diff --git a/pcbnew/dialogs/dialog_cleaning_options.cpp b/pcbnew/dialogs/dialog_cleaning_options.cpp deleted file mode 100644 index 705d93b445..0000000000 --- a/pcbnew/dialogs/dialog_cleaning_options.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @file dialog_cleaning_options.cpp - */ - -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ -#include - -#include - - -DIALOG_CLEANING_OPTIONS::DIALOG_CLEANING_OPTIONS( wxWindow* parent ): - DIALOG_CLEANING_OPTIONS_BASE( parent ) -{ - m_cleanViasOpt->SetValue( m_cleanVias ); - m_mergeSegmOpt->SetValue( m_mergeSegments ); - m_deleteUnconnectedOpt->SetValue( m_deleteUnconnectedSegm ); - m_cleanShortCircuitOpt->SetValue( m_deleteShortCircuits ); - - m_sdbSizerOK->SetDefault(); - GetSizer()->SetSizeHints(this); - Centre(); -} - -// Static members of DIALOG_CLEANING_OPTIONS -bool DIALOG_CLEANING_OPTIONS::m_cleanVias = true; -bool DIALOG_CLEANING_OPTIONS::m_mergeSegments = true; -bool DIALOG_CLEANING_OPTIONS::m_deleteUnconnectedSegm = true; -bool DIALOG_CLEANING_OPTIONS::m_deleteShortCircuits = true; - diff --git a/pcbnew/dialogs/dialog_cleaning_options_base.cpp b/pcbnew/dialogs/dialog_cleaning_options_base.cpp deleted file mode 100644 index eb7ce61f2b..0000000000 --- a/pcbnew/dialogs/dialog_cleaning_options_base.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Nov 22 2017) -// http://www.wxformbuilder.org/ -// -// PLEASE DO *NOT* EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#include "dialog_cleaning_options_base.h" - -/////////////////////////////////////////////////////////////////////////// - -DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - - wxBoxSizer* bSizerMain; - bSizerMain = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizerUpper; - bSizerUpper = new wxBoxSizer( wxVERTICAL ); - - m_cleanShortCircuitOpt = new wxCheckBox( this, wxID_ANY, _("Delete &track segments connecting different nets"), wxDefaultPosition, wxDefaultSize, 0 ); - m_cleanShortCircuitOpt->SetToolTip( _("remove track segments connecting nodes belonging to different nets (short circuit)") ); - - bSizerUpper->Add( m_cleanShortCircuitOpt, 0, wxALL, 5 ); - - m_cleanViasOpt = new wxCheckBox( this, wxID_ANY, _("&Delete redundant vias"), wxDefaultPosition, wxDefaultSize, 0 ); - m_cleanViasOpt->SetToolTip( _("remove vias on through hole pads and superimposed vias") ); - - bSizerUpper->Add( m_cleanViasOpt, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); - - m_mergeSegmOpt = new wxCheckBox( this, wxID_ANY, _("&Merge overlapping segments"), wxDefaultPosition, wxDefaultSize, 0 ); - m_mergeSegmOpt->SetToolTip( _("merge aligned track segments, and remove null segments") ); - - bSizerUpper->Add( m_mergeSegmOpt, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); - - m_deleteUnconnectedOpt = new wxCheckBox( this, wxID_ANY, _("Delete &dangling tracks"), wxDefaultPosition, wxDefaultSize, 0 ); - m_deleteUnconnectedOpt->SetToolTip( _("delete tracks having at least one dangling end") ); - - bSizerUpper->Add( m_deleteUnconnectedOpt, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); - - - bSizerMain->Add( bSizerUpper, 1, wxEXPAND|wxALL, 5 ); - - m_staticline = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerMain->Add( m_staticline, 0, wxEXPAND | wxALL, 5 ); - - m_sdbSizer = new wxStdDialogButtonSizer(); - m_sdbSizerOK = new wxButton( this, wxID_OK ); - m_sdbSizer->AddButton( m_sdbSizerOK ); - m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); - m_sdbSizer->AddButton( m_sdbSizerCancel ); - m_sdbSizer->Realize(); - - bSizerMain->Add( m_sdbSizer, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); - - - this->SetSizer( bSizerMain ); - this->Layout(); - bSizerMain->Fit( this ); - - this->Centre( wxBOTH ); -} - -DIALOG_CLEANING_OPTIONS_BASE::~DIALOG_CLEANING_OPTIONS_BASE() -{ -} diff --git a/pcbnew/dialogs/dialog_cleaning_options_base.h b/pcbnew/dialogs/dialog_cleaning_options_base.h deleted file mode 100644 index fb796cbfcd..0000000000 --- a/pcbnew/dialogs/dialog_cleaning_options_base.h +++ /dev/null @@ -1,53 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Nov 22 2017) -// http://www.wxformbuilder.org/ -// -// PLEASE DO *NOT* EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#ifndef __DIALOG_CLEANING_OPTIONS_BASE_H__ -#define __DIALOG_CLEANING_OPTIONS_BASE_H__ - -#include -#include -#include -#include "dialog_shim.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -/// Class DIALOG_CLEANING_OPTIONS_BASE -/////////////////////////////////////////////////////////////////////////////// -class DIALOG_CLEANING_OPTIONS_BASE : public DIALOG_SHIM -{ - private: - - protected: - wxCheckBox* m_cleanShortCircuitOpt; - wxCheckBox* m_cleanViasOpt; - wxCheckBox* m_mergeSegmOpt; - wxCheckBox* m_deleteUnconnectedOpt; - wxStaticLine* m_staticline; - wxStdDialogButtonSizer* m_sdbSizer; - wxButton* m_sdbSizerOK; - wxButton* m_sdbSizerCancel; - - public: - - DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleaning Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~DIALOG_CLEANING_OPTIONS_BASE(); - -}; - -#endif //__DIALOG_CLEANING_OPTIONS_BASE_H__ diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp new file mode 100644 index 0000000000..f957a9f56c --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp @@ -0,0 +1,221 @@ +/** + * @file dialog_cleaning_options.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dialog_drclistbox.h" + +// Static members of DIALOG_CLEANUP_TRACKS_AND_VIAS +bool DIALOG_CLEANUP_TRACKS_AND_VIAS::m_cleanVias = true; +bool DIALOG_CLEANUP_TRACKS_AND_VIAS::m_mergeSegments = true; +bool DIALOG_CLEANUP_TRACKS_AND_VIAS::m_deleteUnconnectedSegm = true; +bool DIALOG_CLEANUP_TRACKS_AND_VIAS::m_deleteShortCircuits = true; + + +DIALOG_CLEANUP_TRACKS_AND_VIAS::DIALOG_CLEANUP_TRACKS_AND_VIAS( PCB_EDIT_FRAME* aParentFrame ): + DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE( aParentFrame ), + m_parentFrame( aParentFrame ) +{ + m_cleanViasOpt->SetValue( m_cleanVias ); + m_mergeSegmOpt->SetValue( m_mergeSegments ); + m_deleteUnconnectedOpt->SetValue( m_deleteUnconnectedSegm ); + m_cleanShortCircuitOpt->SetValue( m_deleteShortCircuits ); + + // We use a sdbSizer to get platform-dependent ordering of the action buttons, but + // that requires us to correct the button labels here. + m_sdbSizerOK->SetLabel( _( "Update PCB" ) ); + + m_sdbSizerOK->SetDefault(); + GetSizer()->SetSizeHints(this); + Centre(); +} + + +DIALOG_CLEANUP_TRACKS_AND_VIAS::~DIALOG_CLEANUP_TRACKS_AND_VIAS() +{ + m_cleanVias = m_cleanViasOpt->GetValue(); + m_mergeSegments = m_mergeSegmOpt->GetValue(); + m_deleteUnconnectedSegm = m_deleteUnconnectedOpt->GetValue(); + m_deleteShortCircuits = m_cleanShortCircuitOpt->GetValue(); + + for( DRC_ITEM* item : m_items ) + delete item; +} + + +void DIALOG_CLEANUP_TRACKS_AND_VIAS::OnCheckBox( wxCommandEvent& anEvent ) +{ + doCleanup( true ); +} + + +bool DIALOG_CLEANUP_TRACKS_AND_VIAS::TransferDataToWindow() +{ + doCleanup( true ); + + return true; +} + + +bool DIALOG_CLEANUP_TRACKS_AND_VIAS::TransferDataFromWindow() +{ + doCleanup( false ); + + return true; +} + + +void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun ) +{ + for( DRC_ITEM* item : m_items ) + delete item; + + m_items.clear(); + + wxBusyCursor busy; + BOARD_COMMIT commit( m_parentFrame ); + TRACKS_CLEANER cleaner( m_parentFrame->GetUserUnits(), m_parentFrame->GetBoard(), commit ); + + if( !aDryRun ) + { + // Clear current selection list to avoid selection of deleted items + m_parentFrame->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true ); + } + + // Old model has to be refreshed, GAL normally does not keep updating it + m_parentFrame->Compile_Ratsnest( NULL, false ); + + bool modified = cleaner.CleanupBoard( aDryRun, &m_items, + m_cleanShortCircuitOpt->GetValue(), + m_cleanViasOpt->GetValue(), + m_mergeSegmOpt->GetValue(), + m_deleteUnconnectedOpt->GetValue() ); + + if( aDryRun ) + { + m_ItemsListBox->SetList( GetUserUnits(), new DRC_LIST_GENERIC( &m_items ) ); + } + else if( modified ) + { + // Clear undo and redo lists to avoid inconsistencies between lists + m_parentFrame->SetCurItem( NULL ); + commit.Push( _( "Board cleanup" ) ); + + m_parentFrame->GetCanvas()->Refresh( true ); + } +} + + +void DIALOG_CLEANUP_TRACKS_AND_VIAS::OnSelectItem( wxCommandEvent& event ) +{ + int selection = event.GetSelection(); + + if( selection != wxNOT_FOUND ) + { + // Find the selected DRC_ITEM in the listbox, position cursor there. + const DRC_ITEM* item = m_ItemsListBox->GetItem( selection ); + + if( item ) + { + // When selecting a item, center it on GAL and just move the graphic + // cursor in legacy mode gives the best result + bool center = m_parentFrame->IsGalCanvasActive() ? true : false; + m_parentFrame->FocusOnLocation( item->GetPointA(), false, center ); + + WINDOW_THAWER thawer( m_parentFrame ); + m_parentFrame->GetCanvas()->Refresh(); + } + } + + event.Skip(); +} + + +void DIALOG_CLEANUP_TRACKS_AND_VIAS::OnLeftDClickItem( wxMouseEvent& event ) +{ + event.Skip(); + + int selection = m_ItemsListBox->GetSelection(); + + if( selection != wxNOT_FOUND ) + { + // Find the selected DRC_ITEM in the listbox, position cursor there. + // Then hide the dialog. + const DRC_ITEM* item = m_ItemsListBox->GetItem( selection ); + if( item ) + { + // When selecting a item, center it on GAL and just move the graphic + // cursor in legacy mode gives the best result + bool center = m_parentFrame->IsGalCanvasActive() ? true : false; + m_parentFrame->FocusOnLocation( item->GetPointA(), true, center ); + + if( !IsModal() ) + { + Show( false ); + + // We do not want the clarify selection popup when releasing the + // left button in the main window + m_parentFrame->SkipNextLeftButtonReleaseEvent(); + } + } + } +} + + +void DIALOG_CLEANUP_TRACKS_AND_VIAS::OnRightUpItem( wxMouseEvent& event ) +{ + // popup menu to go to either of the items listed in the DRC_ITEM. + + int selection = m_ItemsListBox->GetSelection(); + + if( selection != wxNOT_FOUND ) + { + // popup menu to go to either of the items listed in the DRC_ITEM. + const DRC_ITEM* item = m_ItemsListBox->GetItem( selection ); + GENERAL_COLLECTOR items; + + items.Append( item->GetMainItem( m_parentFrame->GetBoard() ) ); + + if( item->HasSecondItem() ) + items.Append( item->GetAuxiliaryItem( m_parentFrame->GetBoard() ) ); + + WINDOW_THAWER thawer( m_parentFrame ); + m_parentFrame->GetToolManager()->RunAction( PCB_ACTIONS::selectionMenu, true, &items ); + m_parentFrame->GetCanvas()->Refresh(); + } +} + + diff --git a/pcbnew/dialogs/dialog_cleaning_options.h b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h similarity index 58% rename from pcbnew/dialogs/dialog_cleaning_options.h rename to pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h index 5914091a32..80437477eb 100644 --- a/pcbnew/dialogs/dialog_cleaning_options.h +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2010-2014 Jean-Pierre Charras, jean-pierre.charras at wanadoo.fr - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,32 +22,40 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef DIALOG_CLEANING_OPTIONS_H_ -#define DIALOG_CLEANING_OPTIONS_H_ +#ifndef DIALOG_CLEANUP_TRACKS_AND_VIAS_H_ +#define DIALOG_CLEANUP_TRACKS_AND_VIAS_H_ -#include +#include -class DIALOG_CLEANING_OPTIONS: public DIALOG_CLEANING_OPTIONS_BASE +#include + + +class PCB_EDIT_FRAME; + + +class DIALOG_CLEANUP_TRACKS_AND_VIAS: public DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE { -public: + PCB_EDIT_FRAME* m_parentFrame; + DRC_LIST m_items; + static bool m_cleanVias; static bool m_mergeSegments; static bool m_deleteUnconnectedSegm; static bool m_deleteShortCircuits; + void doCleanup( bool aDryRun ); + + void OnCheckBox( wxCommandEvent& anEvent ) override; + void OnSelectItem( wxCommandEvent& event ) override; + void OnLeftDClickItem( wxMouseEvent& event ) override; + void OnRightUpItem( wxMouseEvent& event ) override; + + bool TransferDataToWindow() override; + bool TransferDataFromWindow() override; + public: - DIALOG_CLEANING_OPTIONS( wxWindow* parent ); - - bool TransferDataFromWindow() override - { - m_cleanVias = m_cleanViasOpt->GetValue( ); - m_mergeSegments = m_mergeSegmOpt->GetValue( ); - m_deleteUnconnectedSegm = m_deleteUnconnectedOpt->GetValue( ); - m_deleteShortCircuits = m_cleanShortCircuitOpt->GetValue( ); - - return true; - } + DIALOG_CLEANUP_TRACKS_AND_VIAS( PCB_EDIT_FRAME* parent ); + ~DIALOG_CLEANUP_TRACKS_AND_VIAS(); }; -#endif - // DIALOG_CLEANING_OPTIONS_H_ +#endif // DIALOG_CLEANUP_TRACKS_AND_VIAS_H_ diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.cpp b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.cpp new file mode 100644 index 0000000000..ecb5717eab --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.cpp @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Dec 30 2017) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_drclistbox.h" + +#include "dialog_cleanup_tracks_and_vias_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + wxBoxSizer* bSizerMain; + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + wxBoxSizer* bSizerUpper; + bSizerUpper = new wxBoxSizer( wxVERTICAL ); + + m_cleanShortCircuitOpt = new wxCheckBox( this, wxID_ANY, _("Delete &tracks connecting different nets"), wxDefaultPosition, wxDefaultSize, 0 ); + m_cleanShortCircuitOpt->SetToolTip( _("remove track segments connecting nodes belonging to different nets (short circuit)") ); + + bSizerUpper->Add( m_cleanShortCircuitOpt, 0, wxALL, 5 ); + + m_cleanViasOpt = new wxCheckBox( this, wxID_ANY, _("&Delete redundant vias"), wxDefaultPosition, wxDefaultSize, 0 ); + m_cleanViasOpt->SetToolTip( _("remove vias on through hole pads and superimposed vias") ); + + bSizerUpper->Add( m_cleanViasOpt, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_mergeSegmOpt = new wxCheckBox( this, wxID_ANY, _("&Merge co-linear tracks"), wxDefaultPosition, wxDefaultSize, 0 ); + m_mergeSegmOpt->SetToolTip( _("merge aligned track segments, and remove null segments") ); + + bSizerUpper->Add( m_mergeSegmOpt, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + m_deleteUnconnectedOpt = new wxCheckBox( this, wxID_ANY, _("Delete &dangling tracks"), wxDefaultPosition, wxDefaultSize, 0 ); + m_deleteUnconnectedOpt->SetToolTip( _("delete tracks having at least one dangling end") ); + + bSizerUpper->Add( m_deleteUnconnectedOpt, 0, wxBOTTOM|wxLEFT|wxRIGHT, 5 ); + + + bSizerMain->Add( bSizerUpper, 0, wxEXPAND|wxALL, 5 ); + + wxBoxSizer* bLowerSizer; + bLowerSizer = new wxBoxSizer( wxVERTICAL ); + + bLowerSizer->SetMinSize( wxSize( 660,250 ) ); + staticChangesLabel = new wxStaticText( this, wxID_ANY, _("Changes To Be Applied:"), wxDefaultPosition, wxDefaultSize, 0 ); + staticChangesLabel->Wrap( -1 ); + bLowerSizer->Add( staticChangesLabel, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); + + m_ItemsListBox = new DRCLISTBOX( this, ID_CLEANUP_ITEMS_LIST, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); + bLowerSizer->Add( m_ItemsListBox, 1, wxEXPAND | wxALL, 5 ); + + + bSizerMain->Add( bLowerSizer, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); + m_sdbSizer->AddButton( m_sdbSizerCancel ); + m_sdbSizer->Realize(); + + bSizerMain->Add( m_sdbSizer, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + m_cleanShortCircuitOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_cleanViasOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_mergeSegmOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_deleteUnconnectedOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_ItemsListBox->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnLeftDClickItem ), NULL, this ); + m_ItemsListBox->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnSelectItem ), NULL, this ); + m_ItemsListBox->Connect( wxEVT_RIGHT_UP, wxMouseEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnRightUpItem ), NULL, this ); +} + +DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::~DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE() +{ + // Disconnect Events + m_cleanShortCircuitOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_cleanViasOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_mergeSegmOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_deleteUnconnectedOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnCheckBox ), NULL, this ); + m_ItemsListBox->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnLeftDClickItem ), NULL, this ); + m_ItemsListBox->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnSelectItem ), NULL, this ); + m_ItemsListBox->Disconnect( wxEVT_RIGHT_UP, wxMouseEventHandler( DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE::OnRightUpItem ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_cleaning_options_base.fbp b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp similarity index 71% rename from pcbnew/dialogs/dialog_cleaning_options_base.fbp rename to pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp index 707c217707..fd24beda18 100644 --- a/pcbnew/dialogs/dialog_cleaning_options_base.fbp +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp @@ -11,11 +11,11 @@ res UTF-8 connect - dialog_cleaning_options_base + dialog_cleanup_tracks_and_vias_base 1000 none 1 - dialog_cleaning_options + dialog_cleanup_tracks_and_vias . @@ -42,12 +42,12 @@ wxID_ANY - DIALOG_CLEANING_OPTIONS_BASE + DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE -1,-1 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER DIALOG_SHIM; dialog_shim.h - Cleaning Options + Cleanup Tracks and Vias @@ -96,7 +96,7 @@ 5 wxEXPAND|wxALL - 1 + 0 bSizerUpper @@ -135,7 +135,7 @@ 0 0 wxID_ANY - Delete &track segments connecting different nets + Delete &tracks connecting different nets 0 @@ -165,7 +165,7 @@ - + OnCheckBox @@ -253,7 +253,7 @@ - + OnCheckBox @@ -311,7 +311,7 @@ 0 0 wxID_ANY - &Merge overlapping segments + &Merge co-linear tracks 0 @@ -341,7 +341,7 @@ - + OnCheckBox @@ -429,7 +429,7 @@ - + OnCheckBox @@ -458,83 +458,184 @@ 5 - wxEXPAND | wxALL - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - m_staticline - 1 - - - protected - 1 - - Resizable - 1 - - wxLI_HORIZONTAL - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - + wxEXPAND|wxRIGHT|wxLEFT + 1 + + 660,250 + bLowerSizer + wxVERTICAL + none + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Changes To Be Applied: + + 0 + + + 0 + + 1 + staticChangesLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + ID_CLEANUP_ITEMS_LIST + + 0 + + + 0 + + 1 + m_ItemsListBox + 1 + + + public + 1 + + Resizable + 1 + + + DRCLISTBOX; dialog_drclistbox.h; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + OnLeftDClickItem + + + OnSelectItem + + + + + + + + + + + OnRightUpItem + + + + + diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.h b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.h new file mode 100644 index 0000000000..19dc21d1b9 --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.h @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Dec 30 2017) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE_H__ +#define __DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE_H__ + +#include +#include +#include +class DRCLISTBOX; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +#define ID_CLEANUP_ITEMS_LIST 1000 + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxCheckBox* m_cleanShortCircuitOpt; + wxCheckBox* m_cleanViasOpt; + wxCheckBox* m_mergeSegmOpt; + wxCheckBox* m_deleteUnconnectedOpt; + wxStaticText* staticChangesLabel; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + wxButton* m_sdbSizerCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnCheckBox( wxCommandEvent& event ) { event.Skip(); } + virtual void OnLeftDClickItem( wxMouseEvent& event ) { event.Skip(); } + virtual void OnSelectItem( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRightUpItem( wxMouseEvent& event ) { event.Skip(); } + + + public: + DRCLISTBOX* m_ItemsListBox; + + DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleanup Tracks and Vias"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE(); + +}; + +#endif //__DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE_H__ diff --git a/pcbnew/dialogs/dialog_drclistbox.h b/pcbnew/dialogs/dialog_drclistbox.h index 24cebd5fe3..cf9f263150 100644 --- a/pcbnew/dialogs/dialog_drclistbox.h +++ b/pcbnew/dialogs/dialog_drclistbox.h @@ -107,24 +107,24 @@ public: /** - * Class DRC_LIST_UNCONNECTED + * Class DRC_LIST_GENERIC * is an implementation of the interface named DRC_ITEM_LIST which uses * a vector of pointers to DRC_ITEMs to fulfill the interface. No ownership is taken of the * vector, which will reside in class DRC */ -class DRC_LIST_UNCONNECTED : public DRC_ITEM_LIST +class DRC_LIST_GENERIC : public DRC_ITEM_LIST { DRC_LIST* m_vector; public: - DRC_LIST_UNCONNECTED( DRC_LIST* aList ) : + DRC_LIST_GENERIC( DRC_LIST* aList ) : m_vector(aList) { } /* no destructor since we do not own anything to delete, not even the BOARD. - ~DRC_LIST_UNCONNECTED() {} + ~DRC_LIST_GENERIC() {} */ diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 17ec8053a1..926aa47896 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -568,7 +568,7 @@ void DRC::updatePointers() m_drcDialog->m_ClearanceListBox->SetList( m_pcbEditorFrame->GetUserUnits(), new DRC_LIST_MARKERS( m_pcb ) ); m_drcDialog->m_UnconnectedListBox->SetList( - m_pcbEditorFrame->GetUserUnits(), new DRC_LIST_UNCONNECTED( &m_unconnected ) ); + m_pcbEditorFrame->GetUserUnits(), new DRC_LIST_GENERIC( &m_unconnected ) ); m_drcDialog->UpdateDisplayedCounts(); } @@ -862,6 +862,10 @@ void DRC::testTracks( wxWindow *aActiveWindow, bool aShowProgressBar ) void DRC::testUnconnected() { + for( DRC_ITEM* unconnectedItem : m_unconnected ) + delete unconnectedItem; + + m_unconnected.clear(); auto connectivity = m_pcb->GetConnectivity(); diff --git a/pcbnew/drc.h b/pcbnew/drc.h index 253b60afd5..5141b9ec60 100644 --- a/pcbnew/drc.h +++ b/pcbnew/drc.h @@ -100,6 +100,14 @@ #define DRCE_TRACK_NEAR_EDGE 53 ///< track too close to board edge #define DRCE_INVALID_OUTLINE 54 ///< invalid board outline +#define DRCE_SHORT 58 +#define DRCE_REDUNDANT_VIA 59 +#define DRCE_DUPLICATE_TRACK 60 +#define DRCE_MERGE_TRACKS 61 +#define DRCE_DANGLING_TRACK 62 +#define DRCE_DANGLING_VIA 63 +#define DRCE_ZERO_LENGTH_TRACK 64 + class EDA_DRAW_PANEL; class PCB_EDIT_FRAME; diff --git a/pcbnew/drc_item.cpp b/pcbnew/drc_item.cpp index 1b5f0adb75..c8ec2c5e8e 100644 --- a/pcbnew/drc_item.cpp +++ b/pcbnew/drc_item.cpp @@ -143,6 +143,21 @@ wxString DRC_ITEM::GetErrorText() const case DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT: return wxString( _( "Footprint has incorrect courtyard (not a closed shape)" ) ); + case DRCE_SHORT: + return wxString( _( "Remove track shorting two nets" ) ); + case DRCE_REDUNDANT_VIA: + return wxString( _( "Remove redundant via" ) ); + case DRCE_DUPLICATE_TRACK: + return wxString( _( "Remove duplicate track" ) ); + case DRCE_MERGE_TRACKS: + return wxString( _( "Merge co-linear tracks" ) ); + case DRCE_DANGLING_TRACK: + return wxString( _( "Remove dangling track" ) ); + case DRCE_DANGLING_VIA: + return wxString( _( "Remove dangling via" ) ); + case DRCE_ZERO_LENGTH_TRACK: + return wxString( _( "Remove zero-length track" ) ); + default: return wxString::Format( _( "Unknown DRC error code %d" ), m_ErrorCode ); } diff --git a/pcbnew/pcbnew.h b/pcbnew/pcbnew.h index 5334909fa9..3cfae2970c 100644 --- a/pcbnew/pcbnew.h +++ b/pcbnew/pcbnew.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -54,12 +54,6 @@ #define MATCH_LAYER (1 << 2) ///< if module not on current layer, do not select #define VISIBLE_ONLY (1 << 3) ///< if module not on a visible layer, do not select -/// Flag used in locate routines (from which endpoint work) -enum ENDPOINT_T { - ENDPOINT_START = 0, - ENDPOINT_END = 1 -}; - #define DIM_ANCRE_MODULE 3 // Anchor size (footprint center) diff --git a/pcbnew/tracks_cleaner.cpp b/pcbnew/tracks_cleaner.cpp index 206ff485da..7de3aced3a 100644 --- a/pcbnew/tracks_cleaner.cpp +++ b/pcbnew/tracks_cleaner.cpp @@ -35,7 +35,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -43,133 +44,26 @@ #include #include -// Helper class used to clean tracks and vias -class TRACKS_CLEANER -{ -public: - TRACKS_CLEANER( BOARD* aPcb, BOARD_COMMIT& aCommit ); - - /** - * the cleanup function. - * return true if some item was modified - * @param aCleanVias = true to remove superimposed vias - * @param aRemoveMisConnected = true to remove segments connecting 2 different nets - * @param aMergeSegments = true to merge collinear segmenst and remove 0 len segm - * @param aDeleteUnconnected = true to remove dangling tracks - * (short circuits) - */ - bool CleanupBoard( bool aCleanVias, bool aRemoveMisConnected, - bool aMergeSegments, bool aDeleteUnconnected ); - -private: - /* finds and remove all track segments which are connected to more than one net. - * (short circuits) - */ - bool removeBadTrackSegments(); - - /** - * Removes redundant vias like vias at same location - * or on pad through - */ - bool cleanupVias(); - - /** - * Removes all the following THT vias on the same position of the - * specified one - */ - void removeDuplicatesOfVia( const VIA *aVia, std::set& aToRemove ); - - /** - * Removes all the following duplicates tracks of the specified one - */ - void removeDuplicatesOfTrack( const TRACK* aTrack, std::set& aToRemove ); - - /** - * Removes dangling tracks - */ - bool deleteDanglingTracks(); - - /// Delete null length track segments - bool deleteNullSegments(); - - /// Try to merge the segment to a following collinear one - bool MergeCollinearTracks( TRACK* aSegment ); - - /** - * Merge collinear segments and remove duplicated and null len segments - */ - bool cleanupSegments(); - - /** - * helper function - * Rebuild list of tracks, and connected tracks - * this info must be rebuilt when tracks are erased - */ - void buildTrackConnectionInfo(); - - /** - * helper function - * merge aTrackRef and aCandidate, when possible, - * i.e. when they are colinear, same width, and obviously same layer - */ - TRACK* mergeCollinearSegmentIfPossible( TRACK* aTrackRef, - TRACK* aCandidate, ENDPOINT_T aEndType ); - - const ZONE_CONTAINER* zoneForTrackEndpoint( const TRACK* aTrack, - ENDPOINT_T aEndPoint ); - - bool testTrackEndpointDangling( TRACK* aTrack, ENDPOINT_T aEndPoint ); - - BOARD* m_brd; - BOARD_COMMIT& m_commit; - - bool removeItems( std::set& aItems ) - { - bool isModified = false; - - - for( auto item : aItems ) - { - isModified = true; - m_brd->Remove( item ); - m_commit.Removed( item ); - } - - return isModified; - } -}; +#include /* Install the cleanup dialog frame to know what should be cleaned */ void PCB_EDIT_FRAME::Clean_Pcb() { - DIALOG_CLEANING_OPTIONS dlg( this ); + DIALOG_CLEANUP_TRACKS_AND_VIAS dlg( this ); - if( dlg.ShowModal() != wxID_OK ) - return; + dlg.ShowModal(); +} - // Old model has to be refreshed, GAL normally does not keep updating it - Compile_Ratsnest( NULL, false ); - wxBusyCursor dummy; - BOARD_COMMIT commit( this ); - TRACKS_CLEANER cleaner( GetBoard(), commit ); - - // Clear current selection list to avoid selection of deleted items - GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true ); - - bool modified = cleaner.CleanupBoard( dlg.m_deleteShortCircuits, dlg.m_cleanVias, - dlg.m_mergeSegments, dlg.m_deleteUnconnectedSegm ); - - if( modified ) - { - // Clear undo and redo lists to avoid inconsistencies between lists - SetCurItem( NULL ); - commit.Push( _( "Board cleanup" ) ); - } - - m_canvas->Refresh( true ); +TRACKS_CLEANER::TRACKS_CLEANER( EDA_UNITS_T aUnits, BOARD* aPcb, BOARD_COMMIT& aCommit ) : + m_units( aUnits ), + m_brd( aPcb ), + m_commit( aCommit ), + m_dryRun( true ), + m_itemsList( nullptr ) +{ } @@ -191,14 +85,10 @@ void TRACKS_CLEANER::buildTrackConnectionInfo() for( auto pad : connectivity->GetConnectedPads( track ) ) { if( pad->HitTest( track->GetStart() ) ) - { track->SetState( START_ON_PAD, true ); - } if( pad->HitTest( track->GetEnd() ) ) - { track->SetState( END_ON_PAD, true ); - } } } } @@ -210,12 +100,14 @@ void TRACKS_CLEANER::buildTrackConnectionInfo() * - vias on pad * - null length segments */ -bool TRACKS_CLEANER::CleanupBoard( bool aRemoveMisConnected, +bool TRACKS_CLEANER::CleanupBoard( bool aDryRun, DRC_LIST* aItemsList, + bool aRemoveMisConnected, bool aCleanVias, bool aMergeSegments, bool aDeleteUnconnected ) { - + m_dryRun = aDryRun; + m_itemsList = aItemsList; bool modified = false; // delete redundant vias @@ -253,12 +145,6 @@ bool TRACKS_CLEANER::CleanupBoard( bool aRemoveMisConnected, } -TRACKS_CLEANER::TRACKS_CLEANER( BOARD* aPcb, BOARD_COMMIT& aCommit ) - : m_brd( aPcb ), m_commit( aCommit ) -{ -} - - bool TRACKS_CLEANER::removeBadTrackSegments() { auto connectivity = m_brd->GetConnectivity(); @@ -272,13 +158,31 @@ bool TRACKS_CLEANER::removeBadTrackSegments() for( auto testedPad : connectivity->GetConnectedPads( segment ) ) { if( segment->GetNetCode() != testedPad->GetNetCode() ) + { + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_SHORT, + segment, segment->GetPosition(), + nullptr, wxPoint() ) ); + } + toRemove.insert( segment ); + } } for( auto testedTrack : connectivity->GetConnectedTracks( segment ) ) { if( segment->GetNetCode() != testedTrack->GetNetCode() && !testedTrack->GetState( FLAG0 ) ) + { + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_SHORT, + segment, segment->GetPosition(), + nullptr, wxPoint() ) ); + } + toRemove.insert( segment ); + } } } @@ -294,9 +198,17 @@ void TRACKS_CLEANER::removeDuplicatesOfVia( const VIA *aVia, std::setNext() ); - if( ( alt_via->GetViaType() == VIA_THROUGH ) && - ( alt_via->GetStart() == aVia->GetStart() ) ) + if( ( alt_via->GetViaType() == VIA_THROUGH ) && alt_via->GetStart() == aVia->GetStart() ) + { + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_REDUNDANT_VIA, + alt_via, alt_via->GetPosition(), + nullptr, wxPoint() ) ); + } + aToRemove.insert ( alt_via ); + } } } @@ -305,8 +217,7 @@ bool TRACKS_CLEANER::cleanupVias() { std::set toRemove; - for( VIA* via = GetFirstVia( m_brd->m_Track ); via != NULL; - via = GetFirstVia( via->Next() ) ) + for( VIA* via = GetFirstVia( m_brd->m_Track ); via != NULL; via = GetFirstVia( via->Next() ) ) { if( via->GetFlags() & TRACK_LOCKED ) continue; @@ -335,6 +246,13 @@ bool TRACKS_CLEANER::cleanupVias() if( ( pad->GetLayerSet() & all_cu ) == all_cu ) { + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_REDUNDANT_VIA, + via, via->GetPosition(), + nullptr, wxPoint() ) ); + } + // redundant: delete the via toRemove.insert( via ); break; @@ -416,13 +334,24 @@ bool TRACKS_CLEANER::deleteDanglingTracks() if( flag_erase ) { - m_brd->Remove( track ); - m_commit.Removed( track ); + if( m_itemsList ) + { + int code = track->IsTrack() ? DRCE_DANGLING_TRACK : DRCE_DANGLING_VIA; + m_itemsList->emplace_back( new DRC_ITEM( m_units, code, + track, track->GetPosition(), + nullptr, wxPoint() ) ); + } - /* keep iterating, because a track connected to the deleted track - * now perhaps is not connected and should be deleted */ - item_erased = true; - modified = true; + if( !m_dryRun ) + { + m_brd->Remove( track ); + m_commit.Removed( track ); + + /* keep iterating, because a track connected to the deleted track + * now perhaps is not connected and should be deleted */ + item_erased = true; + modified = true; + } } } } while( item_erased ); @@ -440,41 +369,54 @@ bool TRACKS_CLEANER::deleteNullSegments() for( auto segment : m_brd->Tracks() ) { if( segment->IsNull() ) // Length segment = 0; delete it + { + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_ZERO_LENGTH_TRACK, + segment, segment->GetPosition(), + nullptr, wxPoint() ) ); + } + toRemove.insert( segment ); + } } return removeItems( toRemove ); } -void TRACKS_CLEANER::removeDuplicatesOfTrack( const TRACK *aTrack, std::set& aToRemove ) +void TRACKS_CLEANER::removeDuplicatesOfTrack( const TRACK *aSeg, std::set& aToRemove ) { - if( aTrack->GetFlags() & STRUCT_DELETED ) + if( aSeg->GetFlags() & STRUCT_DELETED ) return; - for( auto other : m_brd->Tracks() ) + for( auto seg2 : m_brd->Tracks() ) { - // New netcode, break out (can't be there any other) - if( aTrack->GetNetCode() != other->GetNetCode() ) + // New netcode, break out (can't be there any seg2) + if( aSeg->GetNetCode() != seg2->GetNetCode() ) continue; - if( aTrack == other ) + if( aSeg == seg2 ) continue; - if( other->GetFlags() & STRUCT_DELETED ) + if( seg2->GetFlags() & STRUCT_DELETED ) continue; - // Must be of the same type, on the same layer and the endpoints - // must be the same (maybe swapped) - if( ( aTrack->Type() == other->Type() ) && - ( aTrack->GetLayer() == other->GetLayer() ) ) + // Must be of the same type, on the same layer and with the same endpoints (although + // they might be swapped) + if( aSeg->Type() == seg2->Type() && aSeg->GetLayer() == seg2->GetLayer() ) { - if( ( ( aTrack->GetStart() == other->GetStart() ) && - ( aTrack->GetEnd() == other->GetEnd() ) ) || - ( ( aTrack->GetStart() == other->GetEnd() ) && - ( aTrack->GetEnd() == other->GetStart() ) ) ) + if( ( aSeg->GetStart() == seg2->GetStart() && aSeg->GetEnd() == seg2->GetEnd() ) || + ( aSeg->GetStart() == seg2->GetEnd() && aSeg->GetEnd() == seg2->GetStart() ) ) { - other->SetFlags( STRUCT_DELETED ); - aToRemove.insert( other ); + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_DUPLICATE_TRACK, + seg2, seg2->GetPosition(), + nullptr, wxPoint() ) ); + } + + seg2->SetFlags( STRUCT_DELETED ); + aToRemove.insert( seg2 ); } } } @@ -485,51 +427,41 @@ bool TRACKS_CLEANER::MergeCollinearTracks( TRACK* aSegment ) { bool merged_this = false; + if( !aSegment->Next() ) + return merged_this; - for( ENDPOINT_T endpoint = ENDPOINT_START; endpoint <= ENDPOINT_END; - endpoint = ENDPOINT_T( endpoint + 1 ) ) + for( ENDPOINT_T endpoint : { ENDPOINT_START, ENDPOINT_END } ) { // search for a possible segment connected to the current endpoint of the current one - TRACK* other = aSegment->Next(); + TRACK* seg2 = aSegment->GetTrack( aSegment->Next(), NULL, endpoint, true, false ); - if( other ) + if( seg2 ) { - other = aSegment->GetTrack( other, NULL, endpoint, true, false ); - - if( other ) + // the two segments must have the same width and seg2 cannot be a via + if( aSegment->GetWidth() == seg2->GetWidth() && seg2->Type() == PCB_TRACE_T ) { - // the two segments must have the same width and the other - // cannot be a via - if( ( aSegment->GetWidth() == other->GetWidth() ) && - ( other->Type() == PCB_TRACE_T ) ) + // There can be only one segment connected + seg2->SetState( BUSY, true ); + TRACK* seg3 = aSegment->GetTrack( m_brd->m_Track, NULL, endpoint, true, false ); + seg2->SetState( BUSY, false ); + + if( seg3 ) + continue; + + // Try to merge them + TRACK* segDelete = mergeCollinearSegments( aSegment, seg2, endpoint ); + + // Merge succesful, seg2 has to go away + if( !m_dryRun && segDelete ) { - // There can be only one segment connected - other->SetState( BUSY, true ); - TRACK* yet_another = aSegment->GetTrack( m_brd->m_Track, NULL, - endpoint, true, false ); - other->SetState( BUSY, false ); - - if( !yet_another ) - { - // Try to merge them - TRACK* segDelete = mergeCollinearSegmentIfPossible( aSegment, - other, endpoint ); - - // Merge succesful, the other one has to go away - if( segDelete ) - { - m_brd->Remove( segDelete ); - m_commit.Removed( segDelete ); - merged_this = true; - } - } + m_brd->Remove( segDelete ); + m_commit.Removed( segDelete ); + merged_this = true; } } } } - - return merged_this; } @@ -619,107 +551,128 @@ static bool parallelismTest( int dx1, int dy1, int dx2, int dy2 ) * and return aCandidate (which can be deleted). * else return NULL */ - static void updateConn( TRACK *track, const std::shared_ptr& connectivity ) { for( auto pad : connectivity->GetConnectedPads( track ) ) { if( pad->HitTest( track->GetStart() ) ) - { track->SetState( START_ON_PAD, true ); - } if( pad->HitTest( track->GetEnd() ) ) - { track->SetState( END_ON_PAD, true ); - } } } -TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate, - ENDPOINT_T aEndType ) +TRACK* TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2, + ENDPOINT_T aEndType ) { // First of all, they must be of the same width and must be both actual tracks - if( ( aTrackRef->GetWidth() != aCandidate->GetWidth() ) || - ( aTrackRef->Type() != PCB_TRACE_T ) || - ( aCandidate->Type() != PCB_TRACE_T ) ) + if( aSeg1->GetWidth() != aSeg2->GetWidth() || + aSeg1->Type() != PCB_TRACE_T || aSeg2->Type() != PCB_TRACE_T ) + { return NULL; + } // Trivial case: exactly the same track - if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) && - ( aTrackRef->GetEnd() == aCandidate->GetEnd() ) ) - return aCandidate; + if( ( aSeg1->GetStart() == aSeg2->GetStart() && aSeg1->GetEnd() == aSeg2->GetEnd() ) || + ( aSeg1->GetStart() == aSeg2->GetEnd() && aSeg1->GetEnd() == aSeg2->GetStart() )) + { + if( m_itemsList ) + { + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_DUPLICATE_TRACK, + aSeg2, aSeg2->GetPosition(), + nullptr, wxPoint() ) ); + } - if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) && - ( aTrackRef->GetEnd() == aCandidate->GetStart() ) ) - return aCandidate; + return aSeg2; + } // Weed out non-parallel tracks - if( !parallelismTest( aTrackRef->GetEnd().x - aTrackRef->GetStart().x, - aTrackRef->GetEnd().y - aTrackRef->GetStart().y, - aCandidate->GetEnd().x - aCandidate->GetStart().x, - aCandidate->GetEnd().y - aCandidate->GetStart().y ) ) + if( !parallelismTest( aSeg1->GetEnd().x - aSeg1->GetStart().x, + aSeg1->GetEnd().y - aSeg1->GetStart().y, + aSeg2->GetEnd().x - aSeg2->GetStart().x, + aSeg2->GetEnd().y - aSeg2->GetStart().y ) ) + { return NULL; + } auto connectivity = m_brd->GetConnectivity(); - updateConn( aTrackRef, connectivity ); - updateConn( aCandidate, connectivity ); + updateConn( aSeg1, connectivity ); + updateConn( aSeg2, connectivity ); - if( aEndType == ENDPOINT_START ) + if( ( aEndType == ENDPOINT_START && aSeg1->GetState( START_ON_PAD ) ) || + ( aEndType == ENDPOINT_END && aSeg1->GetState( END_ON_PAD ) ) ) { // We do not have a pad, which is a always terminal point for a track - if( aTrackRef->GetState( START_ON_PAD ) ) - return NULL; - - /* change the common point coordinate of pt_segm to use the other point - * of pt_segm (pt_segm will be removed later) */ - if( aTrackRef->GetStart() == aCandidate->GetStart() ) - { - m_commit.Modify( aTrackRef ); - aTrackRef->SetStart( aCandidate->GetEnd() ); - aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( END_ON_PAD ) ); - connectivity->Update( aTrackRef ); - return aCandidate; - } - else - { - m_commit.Modify( aTrackRef ); - aTrackRef->SetStart( aCandidate->GetStart() ); - aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( START_ON_PAD ) ); - connectivity->Update( aTrackRef ); - return aCandidate; - } + return NULL; } - else // aEndType == END + + if( m_itemsList ) { - // We do not have a pad, which is a always terminal point for a track - if( aTrackRef->GetState( END_ON_PAD ) ) - return NULL; - - /* change the common point coordinate of pt_segm to use the other point - * of pt_segm (pt_segm will be removed later) */ - if( aTrackRef->GetEnd() == aCandidate->GetStart() ) - { - m_commit.Modify( aTrackRef ); - aTrackRef->SetEnd( aCandidate->GetEnd() ); - aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( END_ON_PAD ) ); - connectivity->Update( aTrackRef ); - - return aCandidate; - } - else - { - m_commit.Modify( aTrackRef ); - aTrackRef->SetEnd( aCandidate->GetStart() ); - aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( START_ON_PAD ) ); - connectivity->Update( aTrackRef ); - return aCandidate; - } + m_itemsList->emplace_back( new DRC_ITEM( m_units, DRCE_MERGE_TRACKS, + aSeg1, aSeg1->GetPosition(), + aSeg2, aSeg2->GetPosition() ) ); } - return NULL; + if( !m_dryRun ) + { + m_commit.Modify( aSeg1 ); + + if( aEndType == ENDPOINT_START ) + { + /* change the common point coordinate of pt_segm to use the other point + * of pt_segm (pt_segm will be removed later) */ + if( aSeg1->GetStart() == aSeg2->GetStart() ) + { + aSeg1->SetStart( aSeg2->GetEnd() ); + aSeg1->SetState( START_ON_PAD, aSeg2->GetState( END_ON_PAD ) ); + } + else + { + aSeg1->SetStart( aSeg2->GetStart() ); + aSeg1->SetState( START_ON_PAD, aSeg2->GetState( START_ON_PAD ) ); + } + } + else // aEndType == END + { + /* change the common point coordinate of pt_segm to use the other point + * of pt_segm (pt_segm will be removed later) */ + if( aSeg1->GetEnd() == aSeg2->GetStart() ) + { + aSeg1->SetEnd( aSeg2->GetEnd() ); + aSeg1->SetState( END_ON_PAD, aSeg2->GetState( END_ON_PAD ) ); + } + else + { + aSeg1->SetEnd( aSeg2->GetStart() ); + aSeg1->SetState( END_ON_PAD, aSeg2->GetState( START_ON_PAD ) ); + } + } + + connectivity->Update( aSeg1 ); + } + + return aSeg2; +} + + +bool TRACKS_CLEANER::removeItems( std::set& aItems ) +{ + if( m_dryRun ) + return false; + + bool isModified = false; + + for( auto item : aItems ) + { + isModified = true; + m_brd->Remove( item ); + m_commit.Removed( item ); + } + + return isModified; } @@ -729,8 +682,8 @@ bool PCB_EDIT_FRAME::RemoveMisConnectedTracks() Compile_Ratsnest( NULL, false ); BOARD_COMMIT commit( this ); - TRACKS_CLEANER cleaner( GetBoard(), commit ); - bool isModified = cleaner.CleanupBoard( true, false, false, false ); + TRACKS_CLEANER cleaner( m_UserUnits, GetBoard(), commit ); + bool isModified = cleaner.CleanupBoard( true, nullptr, true, false, false, false ); if( isModified ) { diff --git a/pcbnew/tracks_cleaner.h b/pcbnew/tracks_cleaner.h new file mode 100644 index 0000000000..f1a7f95f44 --- /dev/null +++ b/pcbnew/tracks_cleaner.h @@ -0,0 +1,118 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef KICAD_TRACKS_CLEANER_H +#define KICAD_TRACKS_CLEANER_H + +#include + +class BOARD; +class BOARD_COMMIT; + + +// Helper class used to clean tracks and vias +class TRACKS_CLEANER +{ +public: + TRACKS_CLEANER( EDA_UNITS_T aUnits, BOARD* aPcb, BOARD_COMMIT& aCommit ); + + /** + * the cleanup function. + * return true if some item was modified + * @param aCleanVias = true to remove superimposed vias + * @param aRemoveMisConnected = true to remove segments connecting 2 different nets + * @param aMergeSegments = true to merge collinear segmenst and remove 0 len segm + * @param aDeleteUnconnected = true to remove dangling tracks + * (short circuits) + */ + bool CleanupBoard( bool aDryRun, DRC_LIST* aItemsList, + bool aCleanVias, bool aRemoveMisConnected, + bool aMergeSegments, bool aDeleteUnconnected ); + +private: + /* finds and remove all track segments which are connected to more than one net. + * (short circuits) + */ + bool removeBadTrackSegments(); + + /** + * Removes redundant vias like vias at same location + * or on pad through + */ + bool cleanupVias(); + + /** + * Removes all the following THT vias on the same position of the + * specified one + */ + void removeDuplicatesOfVia( const VIA *aVia, std::set& aToRemove ); + + /** + * Removes all the following duplicates tracks of the specified one + */ + void removeDuplicatesOfTrack( const TRACK* aSeg, std::set& aToRemove ); + + /** + * Removes dangling tracks + */ + bool deleteDanglingTracks(); + + /// Delete null length track segments + bool deleteNullSegments(); + + /// Try to merge the segment to a following collinear one + bool MergeCollinearTracks( TRACK* aSegment ); + + /** + * Merge collinear segments and remove duplicated and null len segments + */ + bool cleanupSegments(); + + /** + * helper function + * Rebuild list of tracks, and connected tracks + * this info must be rebuilt when tracks are erased + */ + void buildTrackConnectionInfo(); + + /** + * helper function + * merge aTrackRef and aCandidate, when possible, + * i.e. when they are colinear, same width, and obviously same layer + */ + TRACK* mergeCollinearSegments( TRACK* aSeg1, + TRACK* aSeg2, ENDPOINT_T aEndType ); + + bool testTrackEndpointDangling( TRACK* aTrack, ENDPOINT_T aEndPoint ); + + EDA_UNITS_T m_units; + BOARD* m_brd; + BOARD_COMMIT& m_commit; + bool m_dryRun; + DRC_LIST* m_itemsList; + + bool removeItems( std::set& aItems ); +}; + + +#endif //KICAD_TRACKS_CLEANER_H