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
This commit is contained in:
Jeff Young 2019-03-31 10:37:57 +01:00
parent e563184eac
commit 87f303319c
17 changed files with 976 additions and 554 deletions

View File

@ -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

View File

@ -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

View File

@ -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 <wx/wx.h>
#include <dialog_cleaning_options.h>
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;

View File

@ -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()
{
}

View File

@ -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 <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include "dialog_shim.h"
#include <wx/string.h>
#include <wx/checkbox.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/statline.h>
#include <wx/button.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// 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__

View File

@ -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 <wx/wx.h>
#include <dialog_cleanup_tracks_and_vias.h>
#include <wx_html_report_panel.h>
#include <board_commit.h>
#include <tools/pcb_actions.h>
#include <pcb_edit_frame.h>
#include <tracks_cleaner.h>
#include <reporter.h>
#include <class_drawpanel.h>
#include <tool/tool_manager.h>
#include <collectors.h>
#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();
}
}

View File

@ -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 <dialog_cleaning_options_base.h>
#include <dialog_cleanup_tracks_and_vias_base.h>
class DIALOG_CLEANING_OPTIONS: public DIALOG_CLEANING_OPTIONS_BASE
#include <drc.h>
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_

View File

@ -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 );
}

View File

@ -11,11 +11,11 @@
<property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">dialog_cleaning_options_base</property>
<property name="file">dialog_cleanup_tracks_and_vias_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="internationalize">1</property>
<property name="name">dialog_cleaning_options</property>
<property name="name">dialog_cleanup_tracks_and_vias</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
@ -42,12 +42,12 @@
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">DIALOG_CLEANING_OPTIONS_BASE</property>
<property name="name">DIALOG_CLEANUP_TRACKS_AND_VIAS_BASE</property>
<property name="pos"></property>
<property name="size">-1,-1</property>
<property name="style">wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER</property>
<property name="subclass">DIALOG_SHIM; dialog_shim.h</property>
<property name="title">Cleaning Options</property>
<property name="title">Cleanup Tracks and Vias</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
@ -96,7 +96,7 @@
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">1</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizerUpper</property>
@ -135,7 +135,7 @@
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Delete &amp;track segments connecting different nets</property>
<property name="label">Delete &amp;tracks connecting different nets</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
@ -165,7 +165,7 @@
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnCheckBox"></event>
<event name="OnCheckBox">OnCheckBox</event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
@ -253,7 +253,7 @@
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnCheckBox"></event>
<event name="OnCheckBox">OnCheckBox</event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
@ -311,7 +311,7 @@
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">&amp;Merge overlapping segments</property>
<property name="label">&amp;Merge co-linear tracks</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
@ -341,7 +341,7 @@
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnCheckBox"></event>
<event name="OnCheckBox">OnCheckBox</event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
@ -429,7 +429,7 @@
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnCheckBox"></event>
<event name="OnCheckBox">OnCheckBox</event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
@ -458,83 +458,184 @@
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND | wxALL</property>
<property name="proportion">0</property>
<object class="wxStaticLine" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticline</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxLI_HORIZONTAL</property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
<property name="flag">wxEXPAND|wxRIGHT|wxLEFT</property>
<property name="proportion">1</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size">660,250</property>
<property name="name">bLowerSizer</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxTOP|wxRIGHT|wxLEFT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Changes To Be Applied:</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">staticChangesLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxEXPAND | wxALL</property>
<property name="proportion">1</property>
<object class="wxListBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices"></property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">ID_CLEANUP_ITEMS_LIST</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_ItemsListBox</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">public</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">DRCLISTBOX; dialog_drclistbox.h; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick">OnLeftDClickItem</event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnListBox">OnSelectItem</event>
<event name="OnListBoxDClick"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp">OnRightUpItem</event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1">

View File

@ -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 <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class DRCLISTBOX;
#include "dialog_shim.h"
#include <wx/string.h>
#include <wx/checkbox.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/listbox.h>
#include <wx/button.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
#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__

View File

@ -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() {}
*/

View File

@ -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();

View File

@ -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;

View File

@ -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 );
}

View File

@ -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)

View File

@ -35,7 +35,8 @@
#include <pcbnew.h>
#include <class_board.h>
#include <class_track.h>
#include <dialog_cleaning_options.h>
#include <dialog_cleanup_tracks_and_vias.h>
#include <reporter.h>
#include <board_commit.h>
#include <connectivity/connectivity_algo.h>
#include <connectivity/connectivity_data.h>
@ -43,133 +44,26 @@
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
// 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<BOARD_ITEM *>& aToRemove );
/**
* Removes all the following duplicates tracks of the specified one
*/
void removeDuplicatesOfTrack( const TRACK* aTrack, std::set<BOARD_ITEM*>& 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<BOARD_ITEM*>& aItems )
{
bool isModified = false;
for( auto item : aItems )
{
isModified = true;
m_brd->Remove( item );
m_commit.Removed( item );
}
return isModified;
}
};
#include <tracks_cleaner.h>
/* 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::set<BOARD_ITEM
{
next_via = GetFirstVia( alt_via->Next() );
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<BOARD_ITEM*> 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<BOARD_ITEM*>& aToRemove )
void TRACKS_CLEANER::removeDuplicatesOfTrack( const TRACK *aSeg, std::set<BOARD_ITEM*>& 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_DATA>& 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<BOARD_ITEM*>& 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 )
{

118
pcbnew/tracks_cleaner.h Normal file
View File

@ -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_track.h>
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<BOARD_ITEM *>& aToRemove );
/**
* Removes all the following duplicates tracks of the specified one
*/
void removeDuplicatesOfTrack( const TRACK* aSeg, std::set<BOARD_ITEM*>& 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<BOARD_ITEM*>& aItems );
};
#endif //KICAD_TRACKS_CLEANER_H