diff --git a/common/rc_item.cpp b/common/rc_item.cpp index e6fc9dfc6b..8a2abfe253 100644 --- a/common/rc_item.cpp +++ b/common/rc_item.cpp @@ -108,6 +108,8 @@ KIID RC_TREE_MODEL::ToUUID( wxDataViewItem aItem ) case RC_TREE_NODE::MAIN_ITEM: return rc_item->GetMainItemID(); case RC_TREE_NODE::AUX_ITEM: return rc_item->GetAuxItemID(); + case RC_TREE_NODE::AUX_ITEM2: return rc_item->GetAuxItem2ID(); + case RC_TREE_NODE::AUX_ITEM3: return rc_item->GetAuxItem3ID(); } } @@ -162,16 +164,22 @@ void RC_TREE_MODEL::rebuildModel( RC_ITEMS_PROVIDER* aProvider, int aSeverities for( int i = 0; m_rcItemsProvider && i < m_rcItemsProvider->GetCount(); ++i ) { - RC_ITEM* drcItem = m_rcItemsProvider->GetItem( i ); + RC_ITEM* rcItem = m_rcItemsProvider->GetItem( i ); - m_tree.push_back( new RC_TREE_NODE( nullptr, drcItem, RC_TREE_NODE::MARKER ) ); + m_tree.push_back( new RC_TREE_NODE( nullptr, rcItem, RC_TREE_NODE::MARKER ) ); RC_TREE_NODE* n = m_tree.back(); - if( drcItem->GetMainItemID() != niluuid ) - n->m_Children.push_back( new RC_TREE_NODE( n, drcItem, RC_TREE_NODE::MAIN_ITEM ) ); + if( rcItem->GetMainItemID() != niluuid ) + n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::MAIN_ITEM ) ); - if( drcItem->GetAuxItemID() != niluuid ) - n->m_Children.push_back( new RC_TREE_NODE( n, drcItem, RC_TREE_NODE::AUX_ITEM ) ); + if( rcItem->GetAuxItemID() != niluuid ) + n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM ) ); + + if( rcItem->GetAuxItem2ID() != niluuid ) + n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM2 ) ); + + if( rcItem->GetAuxItem3ID() != niluuid ) + n->m_Children.push_back( new RC_TREE_NODE( n, rcItem, RC_TREE_NODE::AUX_ITEM3 ) ); } // Must be called after a significant change of items to force the @@ -256,11 +264,16 @@ void RC_TREE_MODEL::GetValue( wxVariant& aVariant, { case RC_TREE_NODE::MARKER: { - bool excluded = rcItem->GetParent() && rcItem->GetParent()->IsExcluded(); - bool error = m_editFrame->GetSeverity( rcItem->GetErrorCode() ) == RPT_SEVERITY_ERROR; - wxString prefix = wxString::Format( wxT( "%s%s" ), - excluded ? _( "Excluded " ) : wxString( "" ), - error ? _( "Error: " ) : _( "Warning: " ) ); + wxString prefix; + + if( rcItem->GetParent() && rcItem->GetParent()->IsExcluded() ) + prefix = _( "Excluded " ); + + switch( m_editFrame->GetSeverity( rcItem->GetErrorCode() ) ) + { + case RPT_SEVERITY_ERROR: prefix += _( "Error: " ); break; + case RPT_SEVERITY_WARNING: prefix += _( "Warning: " ); break; + } aVariant = prefix + rcItem->GetErrorMessage(); } @@ -269,22 +282,28 @@ void RC_TREE_MODEL::GetValue( wxVariant& aVariant, case RC_TREE_NODE::MAIN_ITEM: { EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetMainItemID() ); - - if( item ) - aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() ); - else - aVariant = _( "item not found (Please, rerun ERC)" ); + aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() ); } break; case RC_TREE_NODE::AUX_ITEM: { EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItemID() ); + aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() ); + } + break; - if( item ) - aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() ); - else - aVariant = _( "item not found (Please, rerun ERC)" ); + case RC_TREE_NODE::AUX_ITEM2: + { + EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItem2ID() ); + aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() ); + } + break; + + case RC_TREE_NODE::AUX_ITEM3: + { + EDA_ITEM* item = m_editFrame->GetItem( rcItem->GetAuxItem3ID() ); + aVariant = item->GetSelectMenuText( m_editFrame->GetUserUnits() ); } break; } diff --git a/common/rc_item.h b/common/rc_item.h index 084659040a..c7e300f312 100644 --- a/common/rc_item.h +++ b/common/rc_item.h @@ -82,6 +82,8 @@ protected: MARKER_BASE* m_parent; // The marker this item belongs to, if any KIID m_mainItemUuid; KIID m_auxItemUuid; + KIID m_auxItem2Uuid; + KIID m_auxItem3Uuid; public: @@ -91,6 +93,8 @@ public: m_parent = nullptr; m_mainItemUuid = niluuid; m_auxItemUuid = niluuid; + m_auxItem2Uuid = niluuid; + m_auxItem3Uuid = niluuid; } RC_ITEM( RC_ITEM* aItem ) @@ -100,28 +104,42 @@ public: m_parent = aItem->m_parent; m_mainItemUuid = aItem->m_mainItemUuid; m_auxItemUuid = aItem->m_auxItemUuid; + m_auxItem2Uuid = aItem->m_auxItem2Uuid; + m_auxItem3Uuid = aItem->m_auxItem3Uuid; } virtual ~RC_ITEM() { } void SetErrorMessage( const wxString& aMessage ) { m_errorMessage = aMessage; } - void SetItems( EDA_ITEM* aItem, EDA_ITEM* bItem = nullptr ) + void SetItems( EDA_ITEM* aItem, EDA_ITEM* bItem = nullptr, EDA_ITEM* cItem = nullptr, + EDA_ITEM* dItem = nullptr ) { m_mainItemUuid = aItem->m_Uuid; if( bItem ) m_auxItemUuid = bItem->m_Uuid; + + if( cItem ) + m_auxItem2Uuid = cItem->m_Uuid; + + if( dItem ) + m_auxItem3Uuid = dItem->m_Uuid; } - void SetItems( const KIID& aItem, const KIID& bItem = niluuid ) + void SetItems( const KIID& aItem, const KIID& bItem = niluuid, const KIID& cItem = niluuid, + const KIID& dItem = niluuid ) { m_mainItemUuid = aItem; m_auxItemUuid = bItem; + m_auxItem2Uuid = cItem; + m_auxItem3Uuid = dItem; } KIID GetMainItemID() const { return m_mainItemUuid; } KIID GetAuxItemID() const { return m_auxItemUuid; } + KIID GetAuxItem2ID() const { return m_auxItem2Uuid; } + KIID GetAuxItem3ID() const { return m_auxItem3Uuid; } void SetParent( MARKER_BASE* aMarker ) { m_parent = aMarker; } MARKER_BASE* GetParent() const { return m_parent; } @@ -161,7 +179,7 @@ public: class RC_TREE_NODE { public: - enum NODE_TYPE { MARKER, MAIN_ITEM, AUX_ITEM }; + enum NODE_TYPE { MARKER, MAIN_ITEM, AUX_ITEM, AUX_ITEM2, AUX_ITEM3 }; RC_TREE_NODE( RC_TREE_NODE* aParent, RC_ITEM* aRcItem, NODE_TYPE aType ) : m_Type( aType ), diff --git a/eeschema/sch_sheet_path.cpp b/eeschema/sch_sheet_path.cpp index 41aba7df40..ca02f2f70a 100644 --- a/eeschema/sch_sheet_path.cpp +++ b/eeschema/sch_sheet_path.cpp @@ -41,6 +41,44 @@ #include #include "erc_item.h" + +/** + * A singleton item of this class is returned for a weak reference that no longer exists. + * Its sole purpose is to flag the item as having been deleted. + */ +class DELETED_SHEET_ITEM : public SCH_ITEM +{ +public: + DELETED_SHEET_ITEM() : + SCH_ITEM( nullptr, NOT_USED ) + {} + + wxString GetSelectMenuText( EDA_UNITS aUnits ) const override + { + return _( "(Deleted Item)" ); + } + + wxString GetClass() const override + { + return wxT( "DELETED_SHEET_ITEM" ); + } + + // pure virtuals: + void SetPosition( const wxPoint& ) override {} + void Print( RENDER_SETTINGS* aSettings, const wxPoint& aOffset ) override {} + void Move( const wxPoint& aMoveVector ) override {} + void MirrorY( int aYaxis_position ) override {} + void MirrorX( int aXaxis_position ) override {} + void Rotate( wxPoint aPosition ) override {} + +#if defined(DEBUG) + void Show( int , std::ostream& ) const override {} +#endif +}; + +DELETED_SHEET_ITEM* g_DeletedItem = nullptr; + + namespace std { size_t hash::operator()( const SCH_SHEET_PATH& path ) const @@ -491,7 +529,11 @@ SCH_ITEM* SCH_SHEET_LIST::GetItem( const KIID& aID, SCH_SHEET_PATH* aPathOut ) } } - return nullptr; + // Not found; weak reference has been deleted. + if( !g_DeletedItem ) + g_DeletedItem = new DELETED_SHEET_ITEM(); + + return g_DeletedItem; } diff --git a/include/pcb_base_frame.h b/include/pcb_base_frame.h index c4e49e27db..794688a3f1 100644 --- a/include/pcb_base_frame.h +++ b/include/pcb_base_frame.h @@ -362,24 +362,18 @@ public: virtual void SwitchLayer( wxDC* DC, PCB_LAYER_ID layer ); - /** - * Function SetActiveLayer - * will change the currently active layer to \a aLayer. - */ virtual void SetActiveLayer( PCB_LAYER_ID aLayer ) { GetScreen()->m_Active_Layer = aLayer; } - /** - * Function GetActiveLayer - * returns the active layer - */ virtual PCB_LAYER_ID GetActiveLayer() const { return GetScreen()->m_Active_Layer; } + int GetSeverity( int aErrorCode ) const override; + void LoadSettings( APP_SETTINGS_BASE* aCfg ) override; void SaveSettings( APP_SETTINGS_BASE* aCfg ) override; diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 85089a94c8..c3faa6a7f1 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -55,6 +55,8 @@ set( PCBNEW_DIALOGS dialogs/dialog_board_statistics.cpp dialogs/dialog_board_statistics_base.cpp dialogs/dialog_choose_footprint.cpp + dialogs/dialog_cleanup_graphics.cpp + dialogs/dialog_cleanup_graphics_base.cpp dialogs/dialog_cleanup_tracks_and_vias.cpp dialogs/dialog_cleanup_tracks_and_vias_base.cpp dialogs/dialog_copper_zones.cpp @@ -262,6 +264,7 @@ set( PCBNEW_CLASS_SRCS array_creator.cpp array_pad_name_provider.cpp build_BOM_from_board.cpp + cleanup_item.cpp cross-probing.cpp edit.cpp edit_track_width.cpp @@ -276,6 +279,7 @@ set( PCBNEW_CLASS_SRCS footprint_viewer_frame.cpp fp_tree_model_adapter.cpp generate_footprint_info.cpp + graphics_cleaner.cpp grid_layer_box_helpers.cpp grid_layer_box_helpers.h initpcb.cpp diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 92a5dae0a9..ed54150acc 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -93,15 +93,6 @@ public: bds.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_IGNORE; } - bds.m_DRCSeverities[ CLEANUP_SHORT ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_REDUNDANT_VIA ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_DUPLICATE_TRACK ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_MERGE_TRACKS ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_DANGLING_TRACK ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_DANGLING_VIA ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_ZERO_LENGTH_TRACK ] = RPT_SEVERITY_ACTION; - bds.m_DRCSeverities[ CLEANUP_TRACK_IN_PAD ] = RPT_SEVERITY_ACTION; - DRC_ITEM drc( 0 ); wxString severity; diff --git a/pcbnew/class_marker_pcb.cpp b/pcbnew/class_marker_pcb.cpp index dbd62b2d1a..a9876056f8 100644 --- a/pcbnew/class_marker_pcb.cpp +++ b/pcbnew/class_marker_pcb.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/pcbnew/cleanup_item.cpp b/pcbnew/cleanup_item.cpp new file mode 100644 index 0000000000..815785f5e6 --- /dev/null +++ b/pcbnew/cleanup_item.cpp @@ -0,0 +1,72 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 KiCad Developers, see change_log.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 + + +CLEANUP_ITEM::CLEANUP_ITEM( int aErrorCode ) +{ + m_errorCode = aErrorCode; +} + + +wxString CLEANUP_ITEM::GetErrorText( int aCode, bool aTranslate ) const +{ + wxString msg; + + if( aCode < 0 ) + aCode = m_errorCode; + + switch( aCode ) + { + // For cleanup tracks and vias: + case CLEANUP_SHORT: msg = _HKI( "Remove track shorting two nets" ); break; + case CLEANUP_REDUNDANT_VIA: msg = _HKI( "Remove redundant via" ); break; + case CLEANUP_DUPLICATE_TRACK: msg = _HKI( "Remove duplicate track" ); break; + case CLEANUP_MERGE_TRACKS: msg = _HKI( "Merge co-linear tracks" ); break; + case CLEANUP_DANGLING_TRACK: msg = _HKI( "Remove dangling track" ); break; + case CLEANUP_DANGLING_VIA: msg = _HKI( "Remove dangling via" ); break; + case CLEANUP_ZERO_LENGTH_TRACK: msg = _HKI( "Remove zero-length track" ); break; + case CLEANUP_TRACK_IN_PAD: msg = _HKI( "Remove track inside pad" ); break; + + // For cleanup graphics: + case CLEANUP_NULL_GRAPHIC: msg = _HKI( "Remove zero-size graphic" ); break; + case CLEANUP_DUPLICATE_GRAPHIC: msg = _HKI( "Remove duplicated graphic" ); break; + case CLEANUP_LINES_TO_RECT: msg = _HKI( "Convert lines to rectangle" ); break; + + default: + wxFAIL_MSG( "Missing cleanup item description" ); + msg = _HKI( "Unknown cleanup action" ); + break; + } + + if( aTranslate ) + return wxGetTranslation( msg ); + else + return msg; +} + + diff --git a/pcbnew/cleanup_item.h b/pcbnew/cleanup_item.h new file mode 100644 index 0000000000..9a7e4a92ad --- /dev/null +++ b/pcbnew/cleanup_item.h @@ -0,0 +1,124 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 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 CLEANUP_ITEM_H +#define CLEANUP_ITEM_H + +#include +#include + +class PCB_BASE_FRAME; + + +enum CLEANUP_RC_CODE { + CLEANUP_FIRST = DRCE_LAST + 1, + CLEANUP_SHORT = CLEANUP_FIRST, + CLEANUP_REDUNDANT_VIA, + CLEANUP_DUPLICATE_TRACK, + CLEANUP_MERGE_TRACKS, + CLEANUP_DANGLING_TRACK, + CLEANUP_DANGLING_VIA, + CLEANUP_ZERO_LENGTH_TRACK, + CLEANUP_TRACK_IN_PAD, + CLEANUP_NULL_GRAPHIC, + CLEANUP_DUPLICATE_GRAPHIC, + CLEANUP_LINES_TO_RECT +}; + +class CLEANUP_ITEM : public RC_ITEM +{ +public: + CLEANUP_ITEM( int aErrorCode ); + + /** + * Function GetErrorText + * returns the string form of a drc error code. + */ + wxString GetErrorText( int aErrorCode = -1, bool aTranslate = true ) const override; + + /** + * Function ShowHtml + * translates this object into a fragment of HTML suitable for the wxHtmlListBox class. + * @return wxString - the html text. + */ + wxString ShowHtml( PCB_BASE_FRAME* aFrame ) const; +}; + + +/** + * VECTOR_CLEANUP_ITEMS_PROVIDER + * is an implementation of the interface named RC_ITEMS_PROVIDER which uses a vector + * of pointers to CLEANUP_ITEMs to fulfill the interface. No ownership is taken of the + * vector. + */ +class VECTOR_CLEANUP_ITEMS_PROVIDER : public RC_ITEMS_PROVIDER +{ + std::vector* m_sourceVector; // owns its CLEANUP_ITEMs + +public: + + VECTOR_CLEANUP_ITEMS_PROVIDER( std::vector* aList ) : + m_sourceVector( aList ) + { + } + + void SetSeverities( int aSeverities ) override + { + } + + int GetCount( int aSeverity = -1 ) override + { + return m_sourceVector->size(); + } + + CLEANUP_ITEM* GetItem( int aIndex ) override + { + return m_sourceVector->at( aIndex ); + } + + void DeleteItem( int aIndex, bool aDeep ) override + { + if( aDeep ) + { + CLEANUP_ITEM* item = m_sourceVector->at( aIndex ); + delete item; + + m_sourceVector->erase( m_sourceVector->begin() + aIndex ); + } + } + + void DeleteAllItems() override + { + if( m_sourceVector ) + { + for( CLEANUP_ITEM* item : *m_sourceVector ) + delete item; + + m_sourceVector->clear(); + } + } +}; + + + +#endif // CLEANUP_ITEM_H diff --git a/pcbnew/dialogs/dialog_cleanup_graphics.cpp b/pcbnew/dialogs/dialog_cleanup_graphics.cpp new file mode 100644 index 0000000000..1b701c4e75 --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_graphics.cpp @@ -0,0 +1,149 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 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 + + +DIALOG_CLEANUP_GRAPHICS::DIALOG_CLEANUP_GRAPHICS( PCB_BASE_FRAME* aParent, bool isModEdit ) : + DIALOG_CLEANUP_GRAPHICS_BASE( aParent ), + m_parentFrame( aParent ), + m_isModEdit( isModEdit ) +{ + m_changesTreeModel = new RC_TREE_MODEL( m_parentFrame, m_changesDataView ); + m_changesDataView->AssociateModel( m_changesTreeModel ); + + m_changesTreeModel->SetSeverities( RPT_SEVERITY_ACTION ); + + // 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( isModEdit ? _( "Update Footprint" ) : _( "Update PCB" ) ); + + m_sdbSizerOK->SetDefault(); + GetSizer()->SetSizeHints(this); + Centre(); +} + + +DIALOG_CLEANUP_GRAPHICS::~DIALOG_CLEANUP_GRAPHICS() +{ + for( CLEANUP_ITEM* item : m_items ) + delete item; + + m_changesTreeModel->DecRef(); +} + + +void DIALOG_CLEANUP_GRAPHICS::OnCheckBox( wxCommandEvent& anEvent ) +{ + doCleanup( true ); +} + + +bool DIALOG_CLEANUP_GRAPHICS::TransferDataToWindow() +{ + doCleanup( true ); + + return true; +} + + +bool DIALOG_CLEANUP_GRAPHICS::TransferDataFromWindow() +{ + doCleanup( false ); + + return true; +} + + +void DIALOG_CLEANUP_GRAPHICS::doCleanup( bool aDryRun ) +{ + wxBusyCursor busy; + + BOARD_COMMIT commit( m_parentFrame ); + BOARD* board = m_parentFrame->GetBoard(); + MODULE* fp = m_isModEdit ? board->GetFirstModule() : nullptr; + GRAPHICS_CLEANER cleaner( fp ? fp->GraphicalItems() : board->Drawings(), fp, commit ); + + if( !aDryRun ) + { + // Clear current selection list to avoid selection of deleted items + m_parentFrame->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true ); + + // ... and to keep the treeModel from trying to refresh a deleted item + m_changesTreeModel->SetProvider( nullptr ); + } + + for( CLEANUP_ITEM* item : m_items ) + delete item; + + m_items.clear(); + + // Old model has to be refreshed, GAL normally does not keep updating it + m_parentFrame->Compile_Ratsnest( false ); + + cleaner.CleanupBoard( aDryRun, &m_items, m_createRectanglesOpt->GetValue(), + m_deleteRedundantOpt->GetValue() ); + + if( aDryRun ) + { + RC_ITEMS_PROVIDER* provider = new VECTOR_CLEANUP_ITEMS_PROVIDER( &m_items ); + m_changesTreeModel->SetProvider( provider ); + } + else if( !commit.Empty() ) + { + // Clear undo and redo lists to avoid inconsistencies between lists + commit.Push( _( "Graphics cleanup" ) ); + m_parentFrame->GetCanvas()->Refresh( true ); + } +} + + +void DIALOG_CLEANUP_GRAPHICS::OnSelectItem( wxDataViewEvent& aEvent ) +{ + const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() ); + BOARD_ITEM* item = m_parentFrame->GetBoard()->GetItem( itemID ); + WINDOW_THAWER thawer( m_parentFrame ); + + m_parentFrame->FocusOnItem( item ); + m_parentFrame->GetCanvas()->Refresh(); + + aEvent.Skip(); +} + + +void DIALOG_CLEANUP_GRAPHICS::OnLeftDClickItem( wxMouseEvent& event ) +{ + event.Skip(); + + if( m_changesDataView->GetCurrentItem().IsOk() ) + { + if( !IsModal() ) + Show( false ); + } +} + + diff --git a/pcbnew/dialogs/dialog_cleanup_graphics.h b/pcbnew/dialogs/dialog_cleanup_graphics.h new file mode 100644 index 0000000000..11ee676f52 --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_graphics.h @@ -0,0 +1,56 @@ +/* + * 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-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 DIALOG_CLEANUP_GRAPHICS_H_ +#define DIALOG_CLEANUP_GRAPHICS_H_ + +#include +#include + + +class PCB_BASE_FRAME; + + +class DIALOG_CLEANUP_GRAPHICS: public DIALOG_CLEANUP_GRAPHICS_BASE +{ + PCB_BASE_FRAME* m_parentFrame; + bool m_isModEdit; + std::vector m_items; + RC_TREE_MODEL* m_changesTreeModel; + + void doCleanup( bool aDryRun ); + + void OnCheckBox( wxCommandEvent& anEvent ) override; + void OnSelectItem( wxDataViewEvent& event ) override; + void OnLeftDClickItem( wxMouseEvent& event ) override; + + bool TransferDataToWindow() override; + bool TransferDataFromWindow() override; + +public: + DIALOG_CLEANUP_GRAPHICS( PCB_BASE_FRAME* aParent, bool isModEdit ); + ~DIALOG_CLEANUP_GRAPHICS(); +}; + +#endif // DIALOG_CLEANUP_GRAPHICS_H_ diff --git a/pcbnew/dialogs/dialog_cleanup_graphics_base.cpp b/pcbnew/dialogs/dialog_cleanup_graphics_base.cpp new file mode 100644 index 0000000000..cd501b9a0c --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_graphics_base.cpp @@ -0,0 +1,76 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 26 2018) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_cleanup_graphics_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_CLEANUP_GRAPHICS_BASE::DIALOG_CLEANUP_GRAPHICS_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_createRectanglesOpt = new wxCheckBox( this, wxID_ANY, _("Merge lines into rectangles"), wxDefaultPosition, wxDefaultSize, 0 ); + m_createRectanglesOpt->SetToolTip( _("remove track segments connecting nodes belonging to different nets (short circuit)") ); + + bSizerUpper->Add( m_createRectanglesOpt, 0, wxALL, 5 ); + + m_deleteRedundantOpt = new wxCheckBox( this, wxID_ANY, _("Delete redundant graphics"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizerUpper->Add( m_deleteRedundantOpt, 0, wxBOTTOM|wxRIGHT|wxLEFT, 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_changesDataView = new wxDataViewCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_NO_HEADER ); + bLowerSizer->Add( m_changesDataView, 1, wxALL|wxEXPAND, 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_createRectanglesOpt->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this ); + m_changesDataView->Connect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnSelectItem ), NULL, this ); + m_changesDataView->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnLeftDClickItem ), NULL, this ); +} + +DIALOG_CLEANUP_GRAPHICS_BASE::~DIALOG_CLEANUP_GRAPHICS_BASE() +{ + // Disconnect Events + m_createRectanglesOpt->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnCheckBox ), NULL, this ); + m_changesDataView->Disconnect( wxEVT_COMMAND_DATAVIEW_SELECTION_CHANGED, wxDataViewEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnSelectItem ), NULL, this ); + m_changesDataView->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( DIALOG_CLEANUP_GRAPHICS_BASE::OnLeftDClickItem ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_cleanup_graphics_base.fbp b/pcbnew/dialogs/dialog_cleanup_graphics_base.fbp new file mode 100644 index 0000000000..2d58c0a783 --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_graphics_base.fbp @@ -0,0 +1,323 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_cleanup_graphics_base + 1000 + none + + 1 + dialog_cleanup_graphics + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + DIALOG_CLEANUP_GRAPHICS_BASE + + -1,-1 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Cleanup Graphics + + + + + + + bSizerMain + wxVERTICAL + none + + 5 + wxEXPAND|wxALL + 0 + + + bSizerUpper + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Merge lines into rectangles + + 0 + + + 0 + + 1 + m_createRectanglesOpt + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + remove track segments connecting nodes belonging to different nets (short circuit) + + wxFILTER_NUMERIC + wxDefaultValidator + + + + + OnCheckBox + + + + 5 + wxBOTTOM|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Delete redundant graphics + + 0 + + + 0 + + 1 + m_deleteRedundantOpt + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + 5 + 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 + + + 0 + + 1 + staticChangesLabel + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + 1 + + + 0 + wxID_ANY + + + m_changesDataView + protected + + + wxDV_NO_HEADER + ; ; forward_declare + + + + + OnSelectItem + OnLeftDClickItem + + + + + + 5 + wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer + protected + + + + + + diff --git a/pcbnew/dialogs/dialog_cleanup_graphics_base.h b/pcbnew/dialogs/dialog_cleanup_graphics_base.h new file mode 100644 index 0000000000..f3a97bd0c9 --- /dev/null +++ b/pcbnew/dialogs/dialog_cleanup_graphics_base.h @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Oct 26 2018) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_CLEANUP_GRAPHICS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_CLEANUP_GRAPHICS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxCheckBox* m_createRectanglesOpt; + wxCheckBox* m_deleteRedundantOpt; + wxStaticText* staticChangesLabel; + wxDataViewCtrl* m_changesDataView; + 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 OnSelectItem( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnLeftDClickItem( wxMouseEvent& event ) { event.Skip(); } + + + public: + + DIALOG_CLEANUP_GRAPHICS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleanup Graphics"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_CLEANUP_GRAPHICS_BASE(); + +}; + diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp index 5a8c86d97c..f96b6b8077 100644 --- a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.cpp @@ -20,23 +20,20 @@ * 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 #include -DIALOG_CLEANUP_TRACKS_AND_VIAS::DIALOG_CLEANUP_TRACKS_AND_VIAS( PCB_EDIT_FRAME* aParentFrame ): +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_parentFrame( aParentFrame ) { auto cfg = m_parentFrame->GetPcbNewSettings(); @@ -71,7 +68,7 @@ DIALOG_CLEANUP_TRACKS_AND_VIAS::~DIALOG_CLEANUP_TRACKS_AND_VIAS() cfg->m_Cleanup.cleanup_short_circuits = m_cleanShortCircuitOpt->GetValue(); cfg->m_Cleanup.cleanup_tracks_in_pad = m_deleteTracksInPadsOpt->GetValue(); - for( DRC_ITEM* item : m_items ) + for( CLEANUP_ITEM* item : m_items ) delete item; m_changesTreeModel->DecRef(); @@ -104,7 +101,7 @@ void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun ) { wxBusyCursor busy; BOARD_COMMIT commit( m_parentFrame ); - TRACKS_CLEANER cleaner( m_parentFrame->GetUserUnits(), m_parentFrame->GetBoard(), commit ); + TRACKS_CLEANER cleaner( m_parentFrame->GetBoard(), commit ); if( !aDryRun ) { @@ -115,7 +112,7 @@ void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun ) m_changesTreeModel->SetProvider( nullptr ); } - for( DRC_ITEM* item : m_items ) + for( CLEANUP_ITEM* item : m_items ) delete item; m_items.clear(); @@ -123,19 +120,18 @@ void DIALOG_CLEANUP_TRACKS_AND_VIAS::doCleanup( bool aDryRun ) // Old model has to be refreshed, GAL normally does not keep updating it m_parentFrame->Compile_Ratsnest( false ); - bool modified = cleaner.CleanupBoard( aDryRun, &m_items, - m_cleanShortCircuitOpt->GetValue(), - m_cleanViasOpt->GetValue(), - m_mergeSegmOpt->GetValue(), - m_deleteUnconnectedOpt->GetValue(), - m_deleteTracksInPadsOpt->GetValue() ); + cleaner.CleanupBoard( aDryRun, &m_items, m_cleanShortCircuitOpt->GetValue(), + m_cleanViasOpt->GetValue(), + m_mergeSegmOpt->GetValue(), + m_deleteUnconnectedOpt->GetValue(), + m_deleteTracksInPadsOpt->GetValue() ); if( aDryRun ) { - RC_ITEMS_PROVIDER* provider = new VECTOR_DRC_ITEMS_PROVIDER( m_parentFrame, &m_items ); + RC_ITEMS_PROVIDER* provider = new VECTOR_CLEANUP_ITEMS_PROVIDER( &m_items ); m_changesTreeModel->SetProvider( provider ); } - else if( modified ) + else if( !commit.Empty() ) { // Clear undo and redo lists to avoid inconsistencies between lists commit.Push( _( "Board cleanup" ) ); diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h index 8a3b5aeba4..2863eac19d 100644 --- a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias.h @@ -26,19 +26,17 @@ #define DIALOG_CLEANUP_TRACKS_AND_VIAS_H_ #include - -#include +#include class PCB_EDIT_FRAME; -class DRC_TREE_MODEL; class DIALOG_CLEANUP_TRACKS_AND_VIAS: public DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE { - PCB_EDIT_FRAME* m_parentFrame; - std::vector m_items; - RC_TREE_MODEL* m_changesTreeModel; + PCB_EDIT_FRAME* m_parentFrame; + std::vector m_items; + RC_TREE_MODEL* m_changesTreeModel; void doCleanup( bool aDryRun ); diff --git a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp index 5f5d71f441..c4ab2c52f4 100644 --- a/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp +++ b/pcbnew/dialogs/dialog_cleanup_tracks_and_vias_base.fbp @@ -58,11 +58,11 @@ bSizerMain wxVERTICAL none - + 5 wxEXPAND|wxALL 0 - + bSizerUpper wxVERTICAL @@ -295,7 +295,7 @@ 0 0 wxID_ANY - Delete &dangling tracks + Delete tracks unconnected at one end 0 @@ -360,7 +360,7 @@ 0 0 wxID_ANY - Delete Tracks in Pads + Delete tracks fully inside pads 0 diff --git a/pcbnew/drc/drc.h b/pcbnew/drc/drc.h index 3b3f07524b..a650740d67 100644 --- a/pcbnew/drc/drc.h +++ b/pcbnew/drc/drc.h @@ -106,17 +106,7 @@ enum PCB_DRC_CODE { DRCE_UNRESOLVED_VARIABLE, - DRCE_LAST = DRCE_UNRESOLVED_VARIABLE, - - // These are actually Cleanup Tracks and Vias actions, not DRCE errors - CLEANUP_SHORT, - CLEANUP_REDUNDANT_VIA, - CLEANUP_DUPLICATE_TRACK, - CLEANUP_MERGE_TRACKS, - CLEANUP_DANGLING_TRACK, - CLEANUP_DANGLING_VIA, - CLEANUP_ZERO_LENGTH_TRACK, - CLEANUP_TRACK_IN_PAD + DRCE_LAST = DRCE_UNRESOLVED_VARIABLE }; diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index ccefd4b977..4cd5fb4a20 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -129,16 +129,6 @@ wxString DRC_ITEM::GetErrorText( int aCode, bool aTranslate ) const case DRCE_MISSING_FOOTPRINT: msg = _HKI( "Missing footprint" ); break; case DRCE_EXTRA_FOOTPRINT: msg = _HKI( "Extra footprint" ); break; - // For cleanup tracks and vias: - case CLEANUP_SHORT: msg = _HKI( "Remove track shorting two nets" ); break; - case CLEANUP_REDUNDANT_VIA: msg = _HKI( "Remove redundant via" ); break; - case CLEANUP_DUPLICATE_TRACK: msg = _HKI( "Remove duplicate track" ); break; - case CLEANUP_MERGE_TRACKS: msg = _HKI( "Merge co-linear tracks" ); break; - case CLEANUP_DANGLING_TRACK: msg = _HKI( "Remove dangling track" ); break; - case CLEANUP_DANGLING_VIA: msg = _HKI( "Remove dangling via" ); break; - case CLEANUP_ZERO_LENGTH_TRACK: msg = _HKI( "Remove zero-length track" ); break; - case CLEANUP_TRACK_IN_PAD: msg = _HKI( "Remove track inside pad" ); break; - case DRCE_UNRESOLVED_VARIABLE: msg = _HKI( "Unresolved text variable" ); break; default: diff --git a/pcbnew/drc/drc_item.h b/pcbnew/drc/drc_item.h index fcdcf9e1c9..3d106750f6 100644 --- a/pcbnew/drc/drc_item.h +++ b/pcbnew/drc/drc_item.h @@ -25,13 +25,9 @@ #ifndef DRC_ITEM_H #define DRC_ITEM_H -#include -#include #include -#include -#include -#include -#include "drc.h" + +class PCB_BASE_FRAME; class DRC_ITEM : public RC_ITEM { diff --git a/pcbnew/drc/drc_provider.h b/pcbnew/drc/drc_provider.h index 9896392d64..f92b44d4c1 100644 --- a/pcbnew/drc/drc_provider.h +++ b/pcbnew/drc/drc_provider.h @@ -27,7 +27,9 @@ #include #include - +#include +#include +#include #include diff --git a/pcbnew/footprint_editor_settings.cpp b/pcbnew/footprint_editor_settings.cpp index 3a5a2846f6..cf892ccc84 100644 --- a/pcbnew/footprint_editor_settings.cpp +++ b/pcbnew/footprint_editor_settings.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include extern const char* traceSettings; diff --git a/pcbnew/graphics_cleaner.cpp b/pcbnew/graphics_cleaner.cpp new file mode 100644 index 0000000000..4a8a9eb151 --- /dev/null +++ b/pcbnew/graphics_cleaner.cpp @@ -0,0 +1,347 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2018 Jean-Pierre Charras, jp.charras at wanadoo.fr + * Copyright (C) 2011 Wayne Stambaugh + * Copyright (C) 1992-2020 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 + + +GRAPHICS_CLEANER::GRAPHICS_CLEANER( DRAWINGS& aDrawings, MODULE* aParentModule, + BOARD_COMMIT& aCommit ) : + m_drawings( aDrawings ), + m_parentModule( aParentModule ), + m_commit( aCommit ), + m_dryRun( true ), + m_itemsList( nullptr ) +{ +} + + +void GRAPHICS_CLEANER::CleanupBoard( bool aDryRun, std::vector* aItemsList, + bool aMergeRects, bool aDeleteRedundant ) +{ + m_dryRun = aDryRun; + m_itemsList = aItemsList; + + // Clear the flag used to mark some segments as deleted, in dry run: + for( BOARD_ITEM* drawing : m_drawings ) + drawing->ClearFlags( IS_DELETED ); + + if( aDeleteRedundant ) + cleanupSegments(); + + if( aMergeRects ) + mergeRects(); + + // Clear the flag used to mark some segments: + for( BOARD_ITEM* drawing : m_drawings ) + drawing->ClearFlags( IS_DELETED ); +} + + +bool GRAPHICS_CLEANER::isNullSegment( DRAWSEGMENT* aSegment ) +{ + switch( aSegment->GetShape() ) + { + case S_SEGMENT: + case S_RECT: + return aSegment->GetStart() == aSegment->GetEnd(); + + case S_CIRCLE: + return aSegment->GetRadius() == 0; + + case S_ARC: + return aSegment->GetCenter().x == aSegment->GetArcStart().x + && aSegment->GetCenter().y == aSegment->GetArcStart().y; + + case S_POLYGON: + return aSegment->GetPointCount() == 0; + + case S_CURVE: + aSegment->RebuildBezierToSegmentsPointsList( aSegment->GetWidth() ); + return aSegment->GetBezierPoints().empty(); + + default: + wxFAIL_MSG( wxString::Format( "unknown DRAWSEGMENT shape: %d", aSegment->GetShape() ) ); + return false; + } +} + + +bool GRAPHICS_CLEANER::areEquivalent( DRAWSEGMENT* aSegment1, DRAWSEGMENT* aSegment2 ) +{ + if( aSegment1->GetShape() != aSegment2->GetShape() + || aSegment1->GetLayer() != aSegment2->GetLayer() + || aSegment1->GetWidth() != aSegment2->GetWidth() ) + { + return false; + } + + switch( aSegment1->GetShape() ) + { + case S_SEGMENT: + case S_RECT: + case S_CIRCLE: + return aSegment1->GetStart() == aSegment2->GetStart() + && aSegment1->GetEnd() == aSegment2->GetEnd(); + + case S_ARC: + return aSegment1->GetCenter() == aSegment2->GetCenter() + && aSegment1->GetArcStart() == aSegment2->GetArcStart() + && aSegment1->GetAngle() == aSegment2->GetAngle(); + + case S_POLYGON: + // TODO + return false; + + case S_CURVE: + return aSegment1->GetBezControl1() == aSegment2->GetBezControl1() + && aSegment1->GetBezControl2() == aSegment2->GetBezControl2() + && aSegment1->GetBezierPoints() == aSegment2->GetBezierPoints(); + + default: + wxFAIL_MSG( wxString::Format( "unknown DRAWSEGMENT shape: %d", aSegment1->GetShape() ) ); + return false; + } +} + + +void GRAPHICS_CLEANER::cleanupSegments() +{ + std::set toRemove; + + // Remove duplicate segments (2 superimposed identical segments): + for( auto it = m_drawings.begin(); it != m_drawings.end(); it++ ) + { + DRAWSEGMENT* segment = dynamic_cast( *it ); + + if( !segment || segment->GetShape() != S_SEGMENT || segment->HasFlag( IS_DELETED ) ) + continue; + + if( isNullSegment( segment ) ) + { + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_NULL_GRAPHIC ); + item->SetItems( segment ); + m_itemsList->push_back( item ); + + toRemove.insert( segment ); + continue; + } + + for( auto it2 = it + 1; it2 != m_drawings.end(); it2++ ) + { + DRAWSEGMENT* segment2 = dynamic_cast( *it2 ); + + if( !segment2 || segment2->HasFlag( IS_DELETED ) ) + continue; + + if( areEquivalent( segment, segment2 ) ) + { + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_DUPLICATE_GRAPHIC ); + item->SetItems( segment2 ); + m_itemsList->push_back( item ); + + segment2->SetFlags( IS_DELETED ); + toRemove.insert( segment2 ); + } + } + } + + if( !m_dryRun ) + removeItems( toRemove ); +} + + +void GRAPHICS_CLEANER::mergeRects() +{ + struct SIDE_CANDIDATE + { + SIDE_CANDIDATE( DRAWSEGMENT* aSeg ) : + start( aSeg->GetStart() ), + end( aSeg->GetEnd() ), + seg( aSeg ) + { + if( start.x > end.x || start.y > end.y ) + std::swap( start, end ); + } + + wxPoint start; + wxPoint end; + DRAWSEGMENT* seg; + }; + + std::set toRemove; + + auto markForRemoval = [&]( SIDE_CANDIDATE* aSide ) + { + toRemove.insert( aSide->seg ); + aSide->seg->SetFlags( IS_DELETED ); + }; + + std::vector sides; + std::map> ptMap; + + // First load all the candidates into the side vector and layer maps + for( BOARD_ITEM* item : m_drawings ) + { + DRAWSEGMENT* seg = dynamic_cast( item ); + + if( !seg || seg->GetShape() != S_SEGMENT ) + continue; + + if( seg->GetStart().x == seg->GetEnd().x || seg->GetStart().y == seg->GetEnd().y ) + { + sides.emplace_back( new SIDE_CANDIDATE( seg ) ); + ptMap[ sides.back()->start ].push_back( sides.back() ); + } + } + + // Now go through the sides and try and match lines into rectangles + for( SIDE_CANDIDATE* side : sides ) + { + if( side->seg->HasFlag( IS_DELETED ) ) + continue; + + SIDE_CANDIDATE* left = nullptr; + SIDE_CANDIDATE* top = nullptr; + SIDE_CANDIDATE* right = nullptr; + SIDE_CANDIDATE* bottom = nullptr; + + auto viable = [&]( SIDE_CANDIDATE* aCandidate ) -> bool + { + return aCandidate->seg->GetLayer() == side->seg->GetLayer() + && aCandidate->seg->GetWidth() == side->seg->GetWidth() + && !aCandidate->seg->HasFlag( IS_DELETED ); + }; + + if( side->start.x == side->end.x ) + { + // We've found a possible left; see if we have a top + // + left = side; + + for( SIDE_CANDIDATE* candidate : ptMap[ left->start ] ) + { + if( candidate != left && viable( candidate ) ) + { + top = candidate; + break; + } + } + } + else if( side->start.y == side->end.y ) + { + // We've found a possible top; see if we have a left + // + top = side; + + for( SIDE_CANDIDATE* candidate : ptMap[ top->start ] ) + { + if( candidate != top && viable( candidate ) ) + { + left = candidate; + break; + } + } + } + + if( top && left ) + { + // See if we can fill in the other two sides + // + for( SIDE_CANDIDATE* candidate : ptMap[ top->end ] ) + { + if( candidate != top && viable( candidate ) ) + { + right = candidate; + break; + } + } + + for( SIDE_CANDIDATE* candidate : ptMap[ left->end ] ) + { + if( candidate != left && viable( candidate ) ) + { + bottom = candidate; + break; + } + } + + if( right && bottom ) + { + markForRemoval( left ); + markForRemoval( top ); + markForRemoval( right ); + markForRemoval( bottom ); + + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_LINES_TO_RECT ); + item->SetItems( left->seg, top->seg, right->seg, bottom->seg ); + m_itemsList->push_back( item ); + + if( !m_dryRun ) + { + DRAWSEGMENT* rect; + + if( m_parentModule ) + rect = new EDGE_MODULE( m_parentModule ); + else + rect = new DRAWSEGMENT(); + + rect->SetShape( S_RECT ); + rect->SetStart( top->start ); + rect->SetEnd( bottom->end ); + rect->SetLayer( top->seg->GetLayer() ); + rect->SetWidth( top->seg->GetWidth() ); + + m_commit.Add( rect ); + } + } + } + } + + if( !m_dryRun ) + removeItems( toRemove ); + + for( SIDE_CANDIDATE* side : sides ) + delete side; +} + + +void GRAPHICS_CLEANER::removeItems( std::set& aItems ) +{ + for( BOARD_ITEM* item : aItems ) + { + if( m_parentModule ) + m_parentModule->Remove( item ); + else + item->GetParent()->Remove( item ); + + m_commit.Removed( item ); + } +} diff --git a/pcbnew/graphics_cleaner.h b/pcbnew/graphics_cleaner.h new file mode 100644 index 0000000000..d01a7e58f1 --- /dev/null +++ b/pcbnew/graphics_cleaner.h @@ -0,0 +1,65 @@ +/* + * 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_GRAPHICS_CLEANER_H +#define KICAD_GRAPHICS_CLEANER_H + +#include + +class MODULE; +class BOARD_COMMIT; +class CLEANUP_ITEM; + + +// Helper class used to clean tracks and vias +class GRAPHICS_CLEANER +{ +public: + GRAPHICS_CLEANER( DRAWINGS& aDrawings, MODULE* aParentModule, BOARD_COMMIT& aCommit ); + + /** + * the cleanup function. + * @param aMergeRects = merge for segments forming a rectangle into a rect + * @param aDeleteRedundant = true to delete null graphics and duplicated graphics + */ + void CleanupBoard( bool aDryRun, std::vector* aItemsList, bool aMergeRects, + bool aDeleteRedundant ); + +private: + bool isNullSegment( DRAWSEGMENT* aSegment ); + bool areEquivalent( DRAWSEGMENT* aSegment1, DRAWSEGMENT* aSegment2 ); + + void cleanupSegments(); + void mergeRects(); + void removeItems( std::set& aItems ); + +private: + DRAWINGS& m_drawings; + MODULE* m_parentModule; // nullptr if not in ModEdit + BOARD_COMMIT& m_commit; + bool m_dryRun; + std::vector* m_itemsList; +}; + + +#endif //KICAD_GRAPHICS_CLEANER_H diff --git a/pcbnew/menubar_footprint_editor.cpp b/pcbnew/menubar_footprint_editor.cpp index 477587398d..e623afa171 100644 --- a/pcbnew/menubar_footprint_editor.cpp +++ b/pcbnew/menubar_footprint_editor.cpp @@ -137,6 +137,9 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar() editMenu->AddItem( PCB_ACTIONS::footprintProperties, haveFootprintCondition ); editMenu->AddItem( PCB_ACTIONS::defaultPadProperties, SELECTION_CONDITIONS::ShowAlways ); + editMenu->AddSeparator(); + editMenu->AddItem( PCB_ACTIONS::cleanupGraphics, haveFootprintCondition ); + editMenu->Resolve(); //-- View menu ------------------------------------------------------- diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp index c73ee9f95b..957e551fcf 100644 --- a/pcbnew/menubar_pcb_editor.cpp +++ b/pcbnew/menubar_pcb_editor.cpp @@ -251,6 +251,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() editMenu->AddItem( ACTIONS::deleteTool, SELECTION_CONDITIONS::ShowAlways ); editMenu->AddItem( PCB_ACTIONS::globalDeletions, SELECTION_CONDITIONS::ShowAlways ); editMenu->AddItem( PCB_ACTIONS::cleanupTracksAndVias, SELECTION_CONDITIONS::ShowAlways ); + editMenu->AddItem( PCB_ACTIONS::cleanupGraphics, SELECTION_CONDITIONS::ShowAlways ); editMenu->Resolve(); diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp index 2f93940ba0..4e7307b533 100644 --- a/pcbnew/pcb_base_frame.cpp +++ b/pcbnew/pcb_base_frame.cpp @@ -51,6 +51,7 @@ #include #include #include +#include "cleanup_item.h" wxDEFINE_EVENT( BOARD_CHANGED, wxCommandEvent ); @@ -662,6 +663,17 @@ void PCB_BASE_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg ) } +int PCB_BASE_FRAME::GetSeverity( int aErrorCode ) const +{ + if( aErrorCode >= CLEANUP_FIRST ) + return RPT_SEVERITY_ACTION; + + BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings(); + + return bds.m_DRCSeverities[ aErrorCode ]; +} + + void PCB_BASE_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg ) { EDA_DRAW_FRAME::SaveSettings( aCfg ); diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index 4e51b7a50d..122f740e37 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -370,14 +370,6 @@ BOARD_ITEM_CONTAINER* PCB_EDIT_FRAME::GetModel() const } -int PCB_EDIT_FRAME::GetSeverity( int aErrorCode ) const -{ - BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings(); - - return bds.m_DRCSeverities[ aErrorCode ]; -} - - void PCB_EDIT_FRAME::SetPageSettings( const PAGE_INFO& aPageSettings ) { PCB_BASE_FRAME::SetPageSettings( aPageSettings ); diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index fdd89cd311..3c8b5b0f84 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -672,9 +672,6 @@ public: ///> @copydoc PCB_BASE_FRAME::GetModel() BOARD_ITEM_CONTAINER* GetModel() const override; - ///> @copydoc EDA_BASE_FRAME::GetSeverity() - int GetSeverity( int aErrorCode ) const override; - ///> @copydoc PCB_BASE_FRAME::SetPageSettings() void SetPageSettings( const PAGE_INFO& aPageSettings ) override; diff --git a/pcbnew/tools/footprint_editor_tools.cpp b/pcbnew/tools/footprint_editor_tools.cpp index 44b045c462..b490d7ce35 100644 --- a/pcbnew/tools/footprint_editor_tools.cpp +++ b/pcbnew/tools/footprint_editor_tools.cpp @@ -27,32 +27,21 @@ #include #include "kicad_clipboard.h" #include "selection_tool.h" -#include "pcb_actions.h" #include #include -#include +#include #include -#include #include -#include -#include #include -#include #include -#include #include #include -#include -#include #include #include #include #include -#include #include -#include -using namespace std::placeholders; -#include +#include FOOTPRINT_EDITOR_TOOLS::FOOTPRINT_EDITOR_TOOLS() : @@ -605,6 +594,17 @@ int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent ) return 0; } + +int FOOTPRINT_EDITOR_TOOLS::CleanupGraphics( const TOOL_EVENT& aEvent ) +{ + FOOTPRINT_EDIT_FRAME* editFrame = getEditFrame(); + DIALOG_CLEANUP_GRAPHICS dlg( editFrame, true ); + + dlg.ShowModal(); + return 0; +} + + void FOOTPRINT_EDITOR_TOOLS::setTransitions() { Go( &FOOTPRINT_EDITOR_TOOLS::NewFootprint, PCB_ACTIONS::newFootprint.MakeEvent() ); @@ -625,6 +625,8 @@ void FOOTPRINT_EDITOR_TOOLS::setTransitions() Go( &FOOTPRINT_EDITOR_TOOLS::ImportFootprint, PCB_ACTIONS::importFootprint.MakeEvent() ); Go( &FOOTPRINT_EDITOR_TOOLS::ExportFootprint, PCB_ACTIONS::exportFootprint.MakeEvent() ); + Go( &FOOTPRINT_EDITOR_TOOLS::CleanupGraphics, PCB_ACTIONS::cleanupGraphics.MakeEvent() ); + Go( &FOOTPRINT_EDITOR_TOOLS::PinLibrary, ACTIONS::pinLibrary.MakeEvent() ); Go( &FOOTPRINT_EDITOR_TOOLS::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() ); Go( &FOOTPRINT_EDITOR_TOOLS::ToggleFootprintTree, PCB_ACTIONS::toggleFootprintTree.MakeEvent() ); diff --git a/pcbnew/tools/footprint_editor_tools.h b/pcbnew/tools/footprint_editor_tools.h index 3339c986dc..a097c5aaf0 100644 --- a/pcbnew/tools/footprint_editor_tools.h +++ b/pcbnew/tools/footprint_editor_tools.h @@ -67,6 +67,8 @@ public: int ToggleFootprintTree( const TOOL_EVENT& aEvent ); int Properties( const TOOL_EVENT& aEvent ); + int CleanupGraphics( const TOOL_EVENT& aEvent ); + /** * Edit the properties used for new pad creation. */ diff --git a/pcbnew/tools/global_edit_tool.cpp b/pcbnew/tools/global_edit_tool.cpp index 90e02b7410..4e6d674f22 100644 --- a/pcbnew/tools/global_edit_tool.cpp +++ b/pcbnew/tools/global_edit_tool.cpp @@ -21,21 +21,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include #include -#include #include #include -#include #include -#include #include +#include #include #include #include - -#include - +#include GLOBAL_EDIT_TOOL::GLOBAL_EDIT_TOOL() : PCB_TOOL_BASE( "pcbnew.GlobalEdit" ), @@ -175,6 +170,26 @@ int GLOBAL_EDIT_TOOL::SwapLayers( const TOOL_EVENT& aEvent ) } +int GLOBAL_EDIT_TOOL::CleanupTracksAndVias( const TOOL_EVENT& aEvent ) +{ + PCB_EDIT_FRAME* editFrame = getEditFrame(); + DIALOG_CLEANUP_TRACKS_AND_VIAS dlg( editFrame ); + + dlg.ShowModal(); + return 0; +} + + +int GLOBAL_EDIT_TOOL::CleanupGraphics( const TOOL_EVENT& aEvent ) +{ + PCB_EDIT_FRAME* editFrame = getEditFrame(); + DIALOG_CLEANUP_GRAPHICS dlg( editFrame, false ); + + dlg.ShowModal(); + return 0; +} + + void GLOBAL_EDIT_TOOL::setTransitions() { Go( &GLOBAL_EDIT_TOOL::ExchangeFootprints, PCB_ACTIONS::updateFootprint.MakeEvent() ); @@ -188,6 +203,7 @@ void GLOBAL_EDIT_TOOL::setTransitions() Go( &GLOBAL_EDIT_TOOL::EditTextAndGraphics, PCB_ACTIONS::editTextAndGraphics.MakeEvent() ); Go( &GLOBAL_EDIT_TOOL::GlobalDeletions, PCB_ACTIONS::globalDeletions.MakeEvent() ); Go( &GLOBAL_EDIT_TOOL::CleanupTracksAndVias, PCB_ACTIONS::cleanupTracksAndVias.MakeEvent() ); + Go( &GLOBAL_EDIT_TOOL::CleanupGraphics, PCB_ACTIONS::cleanupGraphics.MakeEvent() ); } diff --git a/pcbnew/tools/global_edit_tool.h b/pcbnew/tools/global_edit_tool.h index 5d9434b2ca..f151aebc87 100644 --- a/pcbnew/tools/global_edit_tool.h +++ b/pcbnew/tools/global_edit_tool.h @@ -60,6 +60,7 @@ public: int EditTextAndGraphics( const TOOL_EVENT& aEvent ); int GlobalDeletions( const TOOL_EVENT& aEvent ); int CleanupTracksAndVias( const TOOL_EVENT& aEvent ); + int CleanupGraphics( const TOOL_EVENT& aEvent ); private: bool swapBoardItem( BOARD_ITEM* aItem, PCB_LAYER_ID* new_layer ); diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index 013759798c..7187da9105 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -405,9 +405,14 @@ TOOL_ACTION PCB_ACTIONS::globalDeletions( "pcbnew.GlobalEdit.globalDeletions", TOOL_ACTION PCB_ACTIONS::cleanupTracksAndVias( "pcbnew.GlobalEdit.cleanupTracksAndVias", AS_GLOBAL, 0, "", _( "Cleanup Tracks & Vias..." ), - _( "Clean stubs, vias, delete break points or unconnected tracks" ), + _( "Cleanup redundant items, shorting items, etc." ), delete_xpm ); +TOOL_ACTION PCB_ACTIONS::cleanupGraphics( "pcbnew.GlobalEdit.cleanupGraphics", + AS_GLOBAL, 0, "", + _( "Cleanup Graphics..." ), + _( "Cleanup redundant items, etc." ), + delete_xpm ); // MICROWAVE_TOOL // diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index adccf68cac..84f07baf48 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -297,6 +297,7 @@ public: static TOOL_ACTION editTextAndGraphics; static TOOL_ACTION globalDeletions; static TOOL_ACTION cleanupTracksAndVias; + static TOOL_ACTION cleanupGraphics; static TOOL_ACTION updateFootprint; static TOOL_ACTION updateFootprints; static TOOL_ACTION changeFootprint; diff --git a/pcbnew/tracks_cleaner.cpp b/pcbnew/tracks_cleaner.cpp index 51ba0389c3..8995e403b6 100644 --- a/pcbnew/tracks_cleaner.cpp +++ b/pcbnew/tracks_cleaner.cpp @@ -24,14 +24,9 @@ */ #include -#include -#include -#include -#include -#include #include #include -#include +#include #include #include #include @@ -40,23 +35,11 @@ #include -/* Install the cleanup dialog frame to know what should be cleaned -*/ -int GLOBAL_EDIT_TOOL::CleanupTracksAndVias( const TOOL_EVENT& aEvent ) -{ - PCB_EDIT_FRAME* editFrame = getEditFrame(); - DIALOG_CLEANUP_TRACKS_AND_VIAS dlg( editFrame ); - - dlg.ShowModal(); - return 0; -} - - -TRACKS_CLEANER::TRACKS_CLEANER( EDA_UNITS aUnits, BOARD* aPcb, BOARD_COMMIT& aCommit ) - : m_brd( aPcb ), - m_commit( aCommit ), - m_dryRun( true ), - m_itemsList( nullptr ) +TRACKS_CLEANER::TRACKS_CLEANER( BOARD* aPcb, BOARD_COMMIT& aCommit ) : + m_brd( aPcb ), + m_commit( aCommit ), + m_dryRun( true ), + m_itemsList( nullptr ) { } @@ -67,42 +50,39 @@ TRACKS_CLEANER::TRACKS_CLEANER( EDA_UNITS aUnits, BOARD* aPcb, BOARD_COMMIT& aCo * - vias on pad * - null length segments */ -bool TRACKS_CLEANER::CleanupBoard( bool aDryRun, std::vector* aItemsList, +void TRACKS_CLEANER::CleanupBoard( bool aDryRun, std::vector* aItemsList, bool aRemoveMisConnected, bool aCleanVias, bool aMergeSegments, bool aDeleteUnconnected, bool aDeleteTracksinPad ) { m_dryRun = aDryRun; m_itemsList = aItemsList; - bool modified = false; // Clear the flag used to mark some segments as deleted, in dry run: - for( auto segment : m_brd->Tracks() ) + for( TRACK* segment : m_brd->Tracks() ) segment->ClearFlags( IS_DELETED ); // delete redundant vias if( aCleanVias ) - modified |= cleanupVias(); + cleanupVias(); // Remove null segments and intermediate points on aligned segments // If not asked, remove null segments only if remove misconnected is asked if( aMergeSegments ) - modified |= cleanupSegments(); + cleanupSegments(); else if( aRemoveMisConnected ) - modified |= deleteNullSegments( m_brd->Tracks() ); + deleteNullSegments( m_brd->Tracks() ); if( aRemoveMisConnected ) - modified |= removeBadTrackSegments(); + removeBadTrackSegments(); if( aDeleteTracksinPad ) - modified |= deleteTracksInPads(); + deleteTracksInPads(); // Delete dangling tracks if( aDeleteUnconnected ) { if( deleteDanglingTracks() ) { - modified = true; - // Removed tracks can leave aligned segments // (when a T was formed by tracks and the "vertical" segment is removed) if( aMergeSegments ) @@ -111,20 +91,18 @@ bool TRACKS_CLEANER::CleanupBoard( bool aDryRun, std::vector* aItemsL } // Clear the flag used to mark some segments: - for( auto segment : m_brd->Tracks() ) + for( TRACK* segment : m_brd->Tracks() ) segment->ClearFlags( IS_DELETED ); - - return modified; } -bool TRACKS_CLEANER::removeBadTrackSegments() +void TRACKS_CLEANER::removeBadTrackSegments() { - auto connectivity = m_brd->GetConnectivity(); + std::shared_ptr connectivity = m_brd->GetConnectivity(); std::set toRemove; - for( auto segment : m_brd->Tracks() ) + for( TRACK* segment : m_brd->Tracks() ) { segment->SetState( FLAG0, false ); @@ -132,7 +110,7 @@ bool TRACKS_CLEANER::removeBadTrackSegments() { if( segment->GetNetCode() != testedPad->GetNetCode() ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_SHORT ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_SHORT ); item->SetItems( segment ); m_itemsList->push_back( item ); @@ -140,11 +118,11 @@ bool TRACKS_CLEANER::removeBadTrackSegments() } } - for( auto testedTrack : connectivity->GetConnectedTracks( segment ) ) + for( TRACK* testedTrack : connectivity->GetConnectedTracks( segment ) ) { if( segment->GetNetCode() != testedTrack->GetNetCode() && !testedTrack->GetState( FLAG0 ) ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_SHORT ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_SHORT ); item->SetItems( segment ); m_itemsList->push_back( item ); @@ -153,16 +131,17 @@ bool TRACKS_CLEANER::removeBadTrackSegments() } } - return removeItems( toRemove ); + if( !m_dryRun ) + removeItems( toRemove ); } -bool TRACKS_CLEANER::cleanupVias() +void TRACKS_CLEANER::cleanupVias() { std::set toRemove; std::vector vias; - for( auto track : m_brd->Tracks() ) + for( TRACK* track : m_brd->Tracks() ) { if( auto via = dyn_cast( track ) ) vias.push_back( via ); @@ -170,7 +149,7 @@ bool TRACKS_CLEANER::cleanupVias() for( auto via1_it = vias.begin(); via1_it != vias.end(); via1_it++ ) { - auto via1 = *via1_it; + VIA* via1 = *via1_it; if( via1->IsLocked() ) continue; @@ -182,14 +161,15 @@ bool TRACKS_CLEANER::cleanupVias() // Examine the list of connected pads: // if a through pad is found, the via can be removed - const auto pads = m_brd->GetConnectivity()->GetConnectedPads( via1 ); - for( const auto pad : pads ) + const std::vector pads = m_brd->GetConnectivity()->GetConnectedPads( via1 ); + + for( D_PAD* pad : pads ) { const LSET all_cu = LSET::AllCuMask(); if( ( pad->GetLayerSet() & all_cu ) == all_cu ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_REDUNDANT_VIA ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_REDUNDANT_VIA ); item->SetItems( via1, pad ); m_itemsList->push_back( item ); @@ -201,14 +181,14 @@ bool TRACKS_CLEANER::cleanupVias() for( auto via2_it = via1_it + 1; via2_it != vias.end(); via2_it++ ) { - auto via2 = *via2_it; + VIA* via2 = *via2_it; if( via1->GetPosition() != via2->GetPosition() || via2->IsLocked() ) continue; if( via1->GetViaType() == via2->GetViaType() ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_REDUNDANT_VIA ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_REDUNDANT_VIA ); item->SetItems( via1, via2 ); m_itemsList->push_back( item ); @@ -218,8 +198,8 @@ bool TRACKS_CLEANER::cleanupVias() } } - - return removeItems( toRemove ); + if( !m_dryRun ) + removeItems( toRemove ); } @@ -282,7 +262,7 @@ bool TRACKS_CLEANER::deleteDanglingTracks() if( flag_erase ) { int errorCode = track->IsTrack() ? CLEANUP_DANGLING_TRACK : CLEANUP_DANGLING_VIA; - DRC_ITEM* item = new DRC_ITEM( errorCode ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( errorCode ); item->SetItems( track ); m_itemsList->push_back( item ); @@ -307,15 +287,15 @@ bool TRACKS_CLEANER::deleteDanglingTracks() // Delete null length track segments -bool TRACKS_CLEANER::deleteNullSegments( TRACKS& aTracks ) +void TRACKS_CLEANER::deleteNullSegments( TRACKS& aTracks ) { std::set toRemove; - for( auto segment : aTracks ) + for( TRACK* segment : aTracks ) { if( segment->IsNull() && segment->Type() == PCB_TRACE_T && !segment->IsLocked() ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_ZERO_LENGTH_TRACK ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_ZERO_LENGTH_TRACK ); item->SetItems( segment ); m_itemsList->push_back( item ); @@ -323,25 +303,26 @@ bool TRACKS_CLEANER::deleteNullSegments( TRACKS& aTracks ) } } - return removeItems( toRemove ); + if( !m_dryRun ) + removeItems( toRemove ); } -bool TRACKS_CLEANER::deleteTracksInPads() +void TRACKS_CLEANER::deleteTracksInPads() { std::set toRemove; // Delete tracks that start and end on the same pad - auto connectivity = m_brd->GetConnectivity(); + std::shared_ptr connectivity = m_brd->GetConnectivity(); - for( auto track : m_brd->Tracks() ) + for( TRACK* track : m_brd->Tracks() ) { // Mark track if connected to pads - for( auto pad : connectivity->GetConnectedPads( track ) ) + for( D_PAD* pad : connectivity->GetConnectedPads( track ) ) { if( pad->HitTest( track->GetStart() ) && pad->HitTest( track->GetEnd() ) ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_TRACK_IN_PAD ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_TRACK_IN_PAD ); item->SetItems( track ); m_itemsList->push_back( item ); @@ -350,31 +331,30 @@ bool TRACKS_CLEANER::deleteTracksInPads() } } - return removeItems( toRemove ); + if( !m_dryRun ) + removeItems( toRemove ); } // Delete null length segments, and intermediate points .. -bool TRACKS_CLEANER::cleanupSegments() +void TRACKS_CLEANER::cleanupSegments() { - bool modified = false; - // Easy things first - modified |= deleteNullSegments( m_brd->Tracks() ); + deleteNullSegments( m_brd->Tracks() ); std::set toRemove; // Remove duplicate segments (2 superimposed identical segments): for( auto it = m_brd->Tracks().begin(); it != m_brd->Tracks().end(); it++ ) { - auto track1 = *it; + TRACK* track1 = *it; if( track1->Type() != PCB_TRACE_T || track1->HasFlag( IS_DELETED ) || track1->IsLocked() ) continue; for( auto it2 = it + 1; it2 != m_brd->Tracks().end(); it2++ ) { - auto track2 = *it2; + TRACK* track2 = *it2; if( track2->HasFlag( IS_DELETED ) ) continue; @@ -384,7 +364,7 @@ bool TRACKS_CLEANER::cleanupSegments() && track1->GetWidth() == track2->GetWidth() && track1->GetLayer() == track2->GetLayer() ) { - DRC_ITEM* item = new DRC_ITEM( CLEANUP_DUPLICATE_TRACK ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_DUPLICATE_TRACK ); item->SetItems( track2 ); m_itemsList->push_back( item ); @@ -394,7 +374,8 @@ bool TRACKS_CLEANER::cleanupSegments() } } - modified |= removeItems( toRemove ); + if( !m_dryRun ) + removeItems( toRemove ); // Keep a duplicate deque to all deleting in the primary std::deque temp_segments( m_brd->Tracks() ); @@ -412,9 +393,9 @@ bool TRACKS_CLEANER::cleanupSegments() auto& entry = connectivity->GetConnectivityAlgo()->ItemEntry( segment ); - for( auto citem : entry.GetItems() ) + for( CN_ITEM* citem : entry.GetItems() ) { - for( auto connected : citem->ConnectedItems() ) + for( CN_ITEM* connected : citem->ConnectedItems() ) { if( !connected->Valid() ) continue; @@ -431,20 +412,18 @@ bool TRACKS_CLEANER::cleanupSegments() continue; if( segment->ApproxCollinear( *candidateSegment ) ) - modified |= mergeCollinearSegments( segment, candidateSegment ); + mergeCollinearSegments( segment, candidateSegment ); } } } } - - return modified; } -bool TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 ) +void TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 ) { if( aSeg1->IsLocked() || aSeg2->IsLocked() ) - return false; + return; auto connectivity = m_brd->GetConnectivity(); @@ -478,16 +457,16 @@ bool TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 ) if( aSeg1->GetStart() != dummy_seg.GetStart() && aSeg1->GetStart() != dummy_seg.GetEnd() ) { if( testTrackEndpointIsNode( aSeg1, true ) ) - return false; + return; } if( aSeg1->GetEnd() != dummy_seg.GetStart() && aSeg1->GetEnd() != dummy_seg.GetEnd() ) { if( testTrackEndpointIsNode( aSeg1, false ) ) - return false; + return; } - DRC_ITEM* item = new DRC_ITEM( CLEANUP_MERGE_TRACKS ); + CLEANUP_ITEM* item = new CLEANUP_ITEM( CLEANUP_MERGE_TRACKS ); item->SetItems( aSeg1, aSeg2 ); m_itemsList->push_back( item ); @@ -511,21 +490,14 @@ bool TRACKS_CLEANER::mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 ) m_brd->Remove( aSeg2 ); m_commit.Removed( aSeg2 ); } - - return true; } -bool TRACKS_CLEANER::removeItems( std::set& aItems ) +void TRACKS_CLEANER::removeItems( std::set& aItems ) { - if( m_dryRun ) - return false; - for( auto item : aItems ) { m_brd->Remove( item ); m_commit.Removed( item ); } - - return !aItems.empty(); } diff --git a/pcbnew/tracks_cleaner.h b/pcbnew/tracks_cleaner.h index 1396fc5d9e..b2b89f813f 100644 --- a/pcbnew/tracks_cleaner.h +++ b/pcbnew/tracks_cleaner.h @@ -25,60 +25,52 @@ #define KICAD_TRACKS_CLEANER_H #include +#include -class BOARD; class BOARD_COMMIT; +class CLEANUP_ITEM; // Helper class used to clean tracks and vias class TRACKS_CLEANER { public: - TRACKS_CLEANER( EDA_UNITS aUnits, BOARD* aPcb, BOARD_COMMIT& aCommit ); + 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 + * (short circuits) * @param aMergeSegments = true to merge collinear segmenst and remove 0 len segm * @param aDeleteUnconnected = true to remove dangling tracks * @param aDeleteTracksinPad = true to remove tracks fully inside pads - * (short circuits) */ - bool CleanupBoard( bool aDryRun, std::vector* aItemsList, bool aCleanVias, - bool aRemoveMisConnected, bool aMergeSegments, bool aDeleteUnconnected, - bool aDeleteTracksinPad ); + void CleanupBoard( bool aDryRun, std::vector* aItemsList, bool aCleanVias, + bool aRemoveMisConnected, bool aMergeSegments, bool aDeleteUnconnected, + bool aDeleteTracksinPad ); private: - /* finds and remove all track segments which are connected to more than one net. - * (short circuits) + /* + * Removes track segments which are connected to more than one net (short circuits). */ - bool removeBadTrackSegments(); + void removeBadTrackSegments(); /** - * Removes redundant vias like vias at same location - * or on pad through + * Removes redundant vias like vias at same location or on pad through. */ - bool cleanupVias(); + void cleanupVias(); - /** - * Removes dangling tracks - */ bool deleteDanglingTracks(); - /** - * Removes tracks that are fully inside pads - */ - bool deleteTracksInPads(); + void deleteTracksInPads(); - /// Delete null length track segments - bool deleteNullSegments( TRACKS& aTracks ); + void deleteNullSegments( TRACKS& aTracks ); /** - * Merge collinear segments and remove duplicated and null len segments + * Merge collinear segments and remove duplicated and null length segments. */ - bool cleanupSegments(); + void cleanupSegments(); /** * helper function @@ -88,23 +80,23 @@ private: * @param aSeg1 is the reference * @param aSeg2 is the candidate, and after merging, the removed segment */ - bool mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 ); + void mergeCollinearSegments( TRACK* aSeg1, TRACK* aSeg2 ); /** * @return true if a track end position is a node, i.e. a end connected * to more than one item. * @param aTrack is the track to test. - * @param aTstStart = true ot test the start point of the track, and false to - * test the end point + * @param aTstStart = true ot test the start point of the track or false for end point */ bool testTrackEndpointIsNode( TRACK* aTrack, bool aTstStart ); - BOARD* m_brd; - BOARD_COMMIT& m_commit; - bool m_dryRun; - std::vector* m_itemsList; + void removeItems( std::set& aItems ); - bool removeItems( std::set& aItems ); +private: + BOARD* m_brd; + BOARD_COMMIT& m_commit; + bool m_dryRun; + std::vector* m_itemsList; };