From 87f303319cb252fc647da192695deffdd8a0157d Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sun, 31 Mar 2019 10:37:57 +0100 Subject: [PATCH] Implement a dry run for Cleanup Tracks and Vias. Uses the DRC architecture to support inspection of the changes to be made (right click on items, hover over them in the menu to highlight them on the board). Fixes: lp:1571305 * https://bugs.launchpad.net/kicad/+bug/1571305 Fixes: lp:1809474 * https://bugs.launchpad.net/kicad/+bug/1809474 --- pcbnew/CMakeLists.txt | 4 +- pcbnew/class_track.h | 9 +- pcbnew/dialogs/dialog_cleaning_options.cpp | 50 -- .../dialogs/dialog_cleaning_options_base.cpp | 67 --- pcbnew/dialogs/dialog_cleaning_options_base.h | 53 -- .../dialog_cleanup_tracks_and_vias.cpp | 221 ++++++++ ...ons.h => dialog_cleanup_tracks_and_vias.h} | 46 +- .../dialog_cleanup_tracks_and_vias_base.cpp | 98 ++++ ...> dialog_cleanup_tracks_and_vias_base.fbp} | 277 ++++++---- .../dialog_cleanup_tracks_and_vias_base.h | 65 +++ pcbnew/dialogs/dialog_drclistbox.h | 8 +- pcbnew/drc.cpp | 6 +- pcbnew/drc.h | 8 + pcbnew/drc_item.cpp | 15 + pcbnew/pcbnew.h | 8 +- pcbnew/tracks_cleaner.cpp | 477 ++++++++---------- pcbnew/tracks_cleaner.h | 118 +++++ 17 files changed, 976 insertions(+), 554 deletions(-) delete mode 100644 pcbnew/dialogs/dialog_cleaning_options.cpp delete mode 100644 pcbnew/dialogs/dialog_cleaning_options_base.cpp delete mode 100644 pcbnew/dialogs/dialog_cleaning_options_base.h create mode 100644 pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp rename pcbnew/dialogs/{dialog_cleaning_options.h => dialog_cleanup_tracks_and_vias.h} (58%) create mode 100644 pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.cpp rename pcbnew/dialogs/{dialog_cleaning_options_base.fbp => dialog_cleanup_tracks_and_vias_base.fbp} (71%) create mode 100644 pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.h create mode 100644 pcbnew/tracks_cleaner.h 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