Pcbnew, clean board: fix issues and add option in dialog to remove bad track segments (short circuit between 2 nets)

This commit is contained in:
jean-pierre charras 2016-09-30 11:09:17 +02:00
parent 1ebac06d2f
commit b5839893d0
7 changed files with 196 additions and 119 deletions

View File

@ -1,9 +1,9 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2010 Jean-Pierre Charras, jean-pierre.charras@gpisa-lab.inpg.fr
* Copyright (C) 2004-2016 Jean-Pierre Charras, jean-pierre.charras@gpisa-lab.inpg.fr
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 1992-2016 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
@ -51,9 +51,16 @@ public:
/**
* the cleanup function.
* return true if some item was modified
* @param aFrame = the PCB_EDIT_FRAME which handles the board
* @param aRemoveMisConnected = true to remove segments connecting 2 different nets
* @param aCleanVias = true to remove superimposed vias
* @param aMergeSegments = true to merge collinear segmenst and remove 0 len segm
* @param aDeleteUnconnected = true to remove dangling tracks
* (short circuits)
*/
bool CleanupBoard( PCB_EDIT_FRAME *aFrame, bool aCleanVias,
bool aMergeSegments, bool aDeleteUnconnected);
bool aRemoveMisConnected,
bool aMergeSegments, bool aDeleteUnconnected );
private:
@ -77,7 +84,7 @@ private:
/**
* Removes dangling tracks
*/
bool deleteUnconnectedTracks();
bool deleteDanglingTracks();
/// Delete null length track segments
bool delete_null_segments();
@ -121,16 +128,16 @@ void PCB_EDIT_FRAME::Clean_Pcb()
return;
// Old model has to be refreshed, GAL normally does not keep updating it
if( IsGalCanvasActive() )
Compile_Ratsnest( NULL, false );
Compile_Ratsnest( NULL, false );
wxBusyCursor( dummy );
TRACKS_CLEANER cleaner( GetBoard() );
cleaner.CleanupBoard( this, dlg.m_cleanVias, dlg.m_mergeSegments,
dlg.m_deleteUnconnectedSegm );
cleaner.CleanupBoard( this, dlg.m_deleteShortCircuits, dlg.m_cleanVias,
dlg.m_mergeSegments, dlg.m_deleteUnconnectedSegm );
// There is a chance that some of tracks have changed their nets, so rebuild ratsnest from scratch
// There is a chance that some of tracks have changed their nets,
// so rebuild ratsnest from scratch
if( IsGalCanvasActive() )
GetBoard()->GetRatsnest()->ProcessBoard();
@ -143,10 +150,9 @@ void PCB_EDIT_FRAME::Clean_Pcb()
* - Redundant points on tracks (merge aligned segments)
* - vias on pad
* - null length segments
* Create segments when track ends are incorrectly connected:
* i.e. when a track end covers a pad or a via but is not exactly on the pad or the via center
*/
bool TRACKS_CLEANER::CleanupBoard( PCB_EDIT_FRAME *aFrame,
bool aRemoveMisConnected,
bool aCleanVias,
bool aMergeSegments,
bool aDeleteUnconnected )
@ -154,19 +160,24 @@ bool TRACKS_CLEANER::CleanupBoard( PCB_EDIT_FRAME *aFrame,
bool modified = false;
// delete redundant vias
modified |= (aCleanVias && clean_vias());
if( aCleanVias )
modified |= clean_vias();
// Remove null segments and intermediate points on aligned segments
modified |= (aMergeSegments && clean_segments());
if( aMergeSegments )
modified |= clean_segments();
if( aRemoveMisConnected )
modified |= aFrame->RemoveMisConnectedTracks();
// Delete dangling tracks
if( aDeleteUnconnected && deleteUnconnectedTracks() )
if( aDeleteUnconnected && deleteDanglingTracks() )
{
modified = true ;
// Removed tracks can leave aligned segments
// (when a T was formed by tracks and the "vertical" segment
// is removed;
// is removed)
if( aMergeSegments )
clean_segments();
}
@ -207,6 +218,7 @@ void TRACKS_CLEANER::buildTrackConnectionInfo()
// Build connections info tracks to pads
SearchTracksConnectedToPads();
for( TRACK *track = m_Brd->m_Track; track != NULL; track = track->Next() )
{
// Mark track if connected to pads
@ -263,7 +275,7 @@ bool TRACKS_CLEANER::clean_vias()
// Correct via m_End defects (if any), should never happen
if( via->GetStart() != via->GetEnd() )
{
wxFAIL_MSG( wxT( "Via with mismatching ends" ) );
wxFAIL_MSG( "Malformed via with mismatching ends" );
via->SetEnd( via->GetStart() );
}
@ -360,22 +372,23 @@ bool TRACKS_CLEANER::testTrackEndpointDangling( TRACK *aTrack, ENDPOINT_T aEndPo
return flag_erase;
}
/*
* Delete dangling tracks
/* Delete dangling tracks
* Vias:
* If a via is only connected to a dangling track, it also will be removed
*/
bool TRACKS_CLEANER::deleteUnconnectedTracks()
bool TRACKS_CLEANER::deleteDanglingTracks()
{
if( m_Brd->m_Track == NULL )
return false;
bool modified = false;
bool item_erased;
do // Iterate when at least one track is deleted
{
item_erased = false;
TRACK* next_track;
for( TRACK *track = m_Brd->m_Track; track != NULL; track = next_track )
{
next_track = track->Next();
@ -474,7 +487,6 @@ bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
{
bool merged_this = false;
// *WHY* doesn't C++ have prec and succ (or ++ --) like PASCAL?
for( ENDPOINT_T endpoint = ENDPOINT_START; endpoint <= ENDPOINT_END;
endpoint = ENDPOINT_T( endpoint + 1 ) )
{
@ -529,6 +541,7 @@ bool TRACKS_CLEANER::clean_segments()
modified |= delete_null_segments();
// Delete redundant segments, i.e. segments having the same end points and layers
// (can happens when blocks are copied on themselve)
for( TRACK *segment = m_Brd->m_Track; segment; segment = segment->Next() )
modified |= remove_duplicates_of_track( segment );
@ -680,80 +693,73 @@ TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK*
bool PCB_EDIT_FRAME::RemoveMisConnectedTracks()
{
/* finds all track segments which are mis-connected (to more than one net).
* When such a bad segment is found, it is flagged to be removed.
* All tracks having at least one flagged segment are removed.
/* finds all track segments which are connected to more than one net.
* When such a bad segment is found, it is flagged.
* All flagged segments are removed.
*/
TRACK* segment;
TRACK* other;
TRACK* next;
int net_code_s, net_code_e;
bool isModified = false;
Compile_Ratsnest( NULL, false );
for( segment = GetBoard()->m_Track; segment; segment = segment->Next() )
// Rebuild physical connections.
// the list of physical connected items to a given item is in
// m_PadsConnected and m_TracksConnected members of each item
CONNECTIONS connections( GetBoard() );
connections.BuildPadsList();
connections.BuildTracksCandidatesList(GetBoard()->m_Track);
// build connections between track segments and pads.
connections.SearchTracksConnectedToPads();
TRACK* segment;
// build connections between track ends
for( segment = GetBoard()->m_Track; segment; segment = segment->Next() )
{
connections.SearchConnectedTracks( segment );
connections.GetConnectedTracks( segment );
}
bool isModified = false;
for( segment = GetBoard()->m_Track; segment; segment = segment->Next() )
{
segment->SetState( FLAG0, false );
// find the netcode for segment using anything connected to the "start" of "segment"
net_code_s = -1;
if( segment->start && segment->start->Type()==PCB_PAD_T )
for( unsigned ii = 0; ii < segment->m_PadsConnected.size(); ++ii )
{
// get the netcode of the pad to propagate.
net_code_s = ((D_PAD*)(segment->start))->GetNetCode();
}
else
{
other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, false, false );
if( other )
net_code_s = other->GetNetCode();
if( segment->GetNetCode() != segment->m_PadsConnected[ii]->GetNetCode() )
segment->SetState( FLAG0, true );
}
if( net_code_s < 0 )
continue; // the "start" of segment is not connected
// find the netcode for segment using anything connected to the "end" of "segment"
net_code_e = -1;
if( segment->end && segment->end->Type()==PCB_PAD_T )
for( unsigned ii = 0; ii < segment->m_TracksConnected.size(); ++ii )
{
net_code_e = ((D_PAD*)(segment->end))->GetNetCode();
}
else
{
other = segment->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, false, false );
TRACK* tested = segment->m_TracksConnected[ii];
if( other )
net_code_e = other->GetNetCode();
}
if( net_code_e < 0 )
continue; // the "end" of segment is not connected
// Netcodes do not agree, so mark the segment as "to be removed"
if( net_code_s != net_code_e )
{
segment->SetState( FLAG0, true );
if( segment->GetNetCode() != tested->GetNetCode() && !tested->GetState( FLAG0 ) )
segment->SetState( FLAG0, true );
}
}
// Remove tracks having a flagged segment
TRACK* next;
for( segment = GetBoard()->m_Track; segment; segment = next )
{
next = segment->Next();
if( segment->GetState( FLAG0 ) ) // Segment is flagged to be removed
{
segment->SetState( FLAG0, false );
isModified = true;
GetBoard()->m_Status_Pcb = 0;
Remove_One_Track( NULL, segment );
GetBoard()->Remove( segment );
delete segment; // TODO: See if it can be put in undo list
}
}
// the current segment is deleted,
// we do not know the next "not yet tested" segment,
// so restart to the beginning
next = GetBoard()->m_Track;
if( isModified )
{ // some pointers are invalid. Clear the m_TracksConnected list,
// to avoid any issue
for( segment = GetBoard()->m_Track; segment; segment = segment->Next() )
{
segment->m_TracksConnected.clear();
}
}

View File

@ -35,6 +35,7 @@ DIALOG_CLEANING_OPTIONS::DIALOG_CLEANING_OPTIONS( wxWindow* 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);
@ -45,4 +46,5 @@ DIALOG_CLEANING_OPTIONS::DIALOG_CLEANING_OPTIONS( wxWindow* parent ):
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

@ -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-2014 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2016 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
@ -33,32 +33,20 @@ public:
static bool m_cleanVias;
static bool m_mergeSegments;
static bool m_deleteUnconnectedSegm;
static bool m_deleteShortCircuits;
public:
DIALOG_CLEANING_OPTIONS( wxWindow* parent );
~DIALOG_CLEANING_OPTIONS()
bool TransferDataFromWindow() override
{
GetOpts( );
m_cleanVias = m_cleanViasOpt->GetValue( );
m_mergeSegments = m_mergeSegmOpt->GetValue( );
m_deleteUnconnectedSegm = m_deleteUnconnectedOpt->GetValue( );
m_deleteShortCircuits = m_cleanShortCircuitOpt->GetValue( );
return true;
}
private:
void OnCancelClick( wxCommandEvent& event ) override
{
EndModal( wxID_CANCEL );
}
void OnOKClick( wxCommandEvent& event ) override
{
GetOpts( );
EndModal( wxID_OK );
}
void GetOpts( )
{
m_cleanVias = m_cleanViasOpt->GetValue( );
m_mergeSegments = m_mergeSegmOpt->GetValue( );
m_deleteUnconnectedSegm = m_deleteUnconnectedOpt->GetValue( );
}
};
#endif

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Mar 13 2015)
// C++ code generated with wxFormBuilder (version Sep 8 2016)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@ -19,8 +19,13 @@ DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wx
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 pads with a through hole") );
m_cleanViasOpt->SetToolTip( _("remove vias on through hole pads and superimposed vias") );
bSizerUpper->Add( m_cleanViasOpt, 0, wxALL, 5 );
@ -29,8 +34,8 @@ DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wx
bSizerUpper->Add( m_mergeSegmOpt, 0, wxALL, 5 );
m_deleteUnconnectedOpt = new wxCheckBox( this, wxID_ANY, _("D&elete unconnected tracks"), wxDefaultPosition, wxDefaultSize, 0 );
m_deleteUnconnectedOpt->SetToolTip( _("delete track segment having a dangling end") );
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, wxALL, 5 );
@ -54,16 +59,8 @@ DIALOG_CLEANING_OPTIONS_BASE::DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wx
this->Layout();
this->Centre( wxBOTH );
// Connect Events
m_sdbSizerCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CLEANING_OPTIONS_BASE::OnCancelClick ), NULL, this );
m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CLEANING_OPTIONS_BASE::OnOKClick ), NULL, this );
}
DIALOG_CLEANING_OPTIONS_BASE::~DIALOG_CLEANING_OPTIONS_BASE()
{
// Disconnect Events
m_sdbSizerCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CLEANING_OPTIONS_BASE::OnCancelClick ), NULL, this );
m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CLEANING_OPTIONS_BASE::OnOKClick ), NULL, this );
}

View File

@ -44,7 +44,7 @@
<property name="minimum_size"></property>
<property name="name">DIALOG_CLEANING_OPTIONS_BASE</property>
<property name="pos"></property>
<property name="size">257,185</property>
<property name="size">342,179</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>
@ -102,6 +102,94 @@
<property name="name">bSizerUpper</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="wxCheckBox" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="checked">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Delete &amp;track segments connecting different nets</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_cleanShortCircuitOpt</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"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip">remove track segments connecting nodes belonging to different nets (short circuit)</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NUMERIC</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="OnCheckBox"></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">wxALL</property>
@ -156,7 +244,7 @@
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip">remove vias on pads with a through hole</property>
<property name="tooltip">remove vias on through hole pads and superimposed vias</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NUMERIC</property>
<property name="validator_type">wxDefaultValidator</property>
@ -311,7 +399,7 @@
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">D&amp;elete unconnected tracks</property>
<property name="label">Delete &amp;dangling tracks</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
@ -332,7 +420,7 @@
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip">delete track segment having a dangling end</property>
<property name="tooltip">delete tracks having at least one dangling end</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NUMERIC</property>
<property name="validator_type">wxDefaultValidator</property>
@ -466,11 +554,11 @@
<property name="name">m_sdbSizer</property>
<property name="permission">protected</property>
<event name="OnApplyButtonClick"></event>
<event name="OnCancelButtonClick">OnCancelClick</event>
<event name="OnCancelButtonClick"></event>
<event name="OnContextHelpButtonClick"></event>
<event name="OnHelpButtonClick"></event>
<event name="OnNoButtonClick"></event>
<event name="OnOKButtonClick">OnOKClick</event>
<event name="OnOKButtonClick"></event>
<event name="OnSaveButtonClick"></event>
<event name="OnYesButtonClick"></event>
</object>

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Mar 13 2015)
// C++ code generated with wxFormBuilder (version Sep 8 2016)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
@ -36,6 +36,7 @@ class DIALOG_CLEANING_OPTIONS_BASE : public DIALOG_SHIM
private:
protected:
wxCheckBox* m_cleanShortCircuitOpt;
wxCheckBox* m_cleanViasOpt;
wxCheckBox* m_mergeSegmOpt;
wxCheckBox* m_deleteUnconnectedOpt;
@ -43,15 +44,10 @@ class DIALOG_CLEANING_OPTIONS_BASE : public DIALOG_SHIM
wxStdDialogButtonSizer* m_sdbSizer;
wxButton* m_sdbSizerOK;
wxButton* m_sdbSizerCancel;
// Virtual event handlers, overide them in your derived class
virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOKClick( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleaning Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 257,185 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
DIALOG_CLEANING_OPTIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Cleaning Options"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 342,179 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_CLEANING_OPTIONS_BASE();
};

View File

@ -431,8 +431,8 @@ void PCB_BASE_EDIT_FRAME::PutDataInPreviousState( PICKED_ITEMS_LIST* aList, bool
switch( item->Type() )
{
case PCB_MODULE_T:
deep_reBuild_ratsnest = true;
// Fall though
deep_reBuild_ratsnest = true; // Pointers on pads can be invalid
// Fall through
case PCB_ZONE_AREA_T:
case PCB_TRACE_T:
case PCB_VIA_T: