kicad/pcbnew/clean.cpp

810 lines
26 KiB
C++
Raw Normal View History

/*
* 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) 2011 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 1992-2011 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
*/
/**
* @file clean.cpp
* @brief functions to clean tracks: remove null lenght and redundant segments
*/
2007-05-06 16:03:28 +00:00
#include <fctsys.h>
#include <class_drawpanel.h>
#include <pcbcommon.h>
#include <wxPcbStruct.h>
#include <pcbnew.h>
#include <class_board.h>
#include <class_track.h>
#include <connect.h>
#include <dialog_cleaning_options.h>
// Helper class used to clean tracks and vias
class TRACKS_CLEANER: CONNECTIONS
{
private:
BOARD * m_Brd;
bool m_deleteUnconnectedTracks;
bool m_mergeSegments;
bool m_cleanVias;
public:
TRACKS_CLEANER( BOARD * aPcb );
/**
* the cleanup function.
* return true if some item was modified
*/
bool CleanupBoard();
2007-05-06 16:03:28 +00:00
void SetdeleteUnconnectedTracksOpt( bool aDelete )
{
m_deleteUnconnectedTracks = aDelete;
}
void SetMergeSegmentsOpt( bool aMerge )
{
m_mergeSegments = aMerge;
}
void SetCleanViasOpt( bool aClean )
{
m_cleanVias = aClean;
}
private:
2007-05-06 16:03:28 +00:00
/**
* Removes redundant vias like vias at same location
* or on pad through
*/
bool clean_vias();
/**
* Removes dangling tracks
*/
bool deleteUnconnectedTracks();
/**
* Merge colinear segments and remove null len segments
*/
bool clean_segments();
/**
* 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, int aEndType );
};
2007-10-19 23:02:11 +00:00
/* Install the cleanup dialog frame to know what should be cleaned
*/
void PCB_EDIT_FRAME::Clean_Pcb()
2007-05-06 16:03:28 +00:00
{
DIALOG_CLEANING_OPTIONS dlg( this );
if( dlg.ShowModal() != wxID_OK )
return;
wxBusyCursor( dummy );
TRACKS_CLEANER cleaner( GetBoard() );
cleaner.SetdeleteUnconnectedTracksOpt( dlg.m_deleteUnconnectedSegm );
cleaner.SetMergeSegmentsOpt( dlg.m_mergeSegments );
cleaner.SetCleanViasOpt( dlg.m_cleanVias );
if( cleaner.CleanupBoard() )
{
// Clear undo and redo lists to avoid inconsistencies between lists
GetScreen()->ClearUndoRedoList();
SetCurItem( NULL );
Compile_Ratsnest( NULL, true );
OnModify();
}
m_canvas->Refresh( true );
}
2007-08-10 19:14:51 +00:00
/* Main cleaning function.
* Delete
* - Redundant points on tracks (merge aligned segments)
* - vias on pad
* - null lenght 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()
{
bool modified = false;
// delete redundant vias
if( m_cleanVias && clean_vias() )
modified = true;
2008-03-10 15:00:22 +00:00
// Remove null segments and intermediate points on aligned segments
if( m_mergeSegments && clean_segments() )
modified = true;
2009-08-03 07:55:08 +00:00
// Delete dangling tracks
if( m_deleteUnconnectedTracks && deleteUnconnectedTracks() )
modified = true;
2007-08-10 19:14:51 +00:00
return modified;
}
TRACKS_CLEANER::TRACKS_CLEANER( BOARD * aPcb ): CONNECTIONS( aPcb )
{
m_Brd = aPcb;
m_deleteUnconnectedTracks = false;
m_mergeSegments = false;
// Build connections info
BuildPadsList();
buildTrackConnectionInfo();
}
2007-08-10 19:14:51 +00:00
void TRACKS_CLEANER::buildTrackConnectionInfo()
{
BuildTracksCandidatesList( m_Brd->m_Track, NULL);
2007-05-06 16:03:28 +00:00
// clear flags and variables used in cleanup
for( TRACK * track = m_Brd->m_Track; track; track = track->Next() )
{
track->start = NULL;
track->end = NULL;
track->m_PadsConnected.clear();
track->SetState( START_ON_PAD|END_ON_PAD|BUSY, false );
}
2007-05-06 16:03:28 +00:00
// Build connections info tracks to pads
SearchTracksConnectedToPads();
for( TRACK * track = m_Brd->m_Track; track; track = track->Next() )
{
// Mark track if connected to pads
for( unsigned jj = 0; jj < track->m_PadsConnected.size(); jj++ )
{
D_PAD * pad = track->m_PadsConnected[jj];
2007-05-06 16:03:28 +00:00
if( pad->HitTest( track->GetStart() ) )
{
track->start = pad;
track->SetState( START_ON_PAD, true );
}
2007-05-06 16:03:28 +00:00
if( pad->HitTest( track->GetEnd() ) )
{
track->end = pad;
track->SetState( END_ON_PAD, true );
}
}
}
2007-05-06 16:03:28 +00:00
}
bool TRACKS_CLEANER::clean_vias()
{
TRACK* next_track;
bool modified = false;
for( TRACK* track = m_Brd->m_Track; track; track = track->Next() )
{
// Correct via m_End defects (if any)
if( track->Type() == PCB_VIA_T )
{
if( track->GetStart() != track->GetEnd() )
track->SetEnd( track->GetStart() );
}
2011-12-14 04:29:25 +00:00
if( track->GetShape() != VIA_THROUGH )
continue;
// Search and delete others vias at same location
TRACK* alt_track = track->Next();
for( ; alt_track != NULL; alt_track = next_track )
{
next_track = alt_track->Next();
if( alt_track->GetShape() != VIA_THROUGH )
continue;
if( alt_track->GetStart() != track->GetStart() )
continue;
2012-02-19 04:02:19 +00:00
// delete via
alt_track->UnLink();
delete alt_track;
modified = true;
}
}
2012-02-19 04:02:19 +00:00
// Delete Via on pads at same location
for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track )
{
next_track = track->Next();
if( track->GetShape() != VIA_THROUGH )
continue;
// Examine the list of connected pads:
// if one pad through is found, the via can be removed
for( unsigned ii = 0; ii < track->m_PadsConnected.size(); ii++ )
{
D_PAD * pad = track->m_PadsConnected[ii];
if( (pad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS )
{
// redundant: via delete it
track->UnLink();
delete track;
modified = true;
break;
}
}
}
return modified;
}
2007-05-06 16:03:28 +00:00
/*
* Delete dangling tracks
* Vias:
* If a via is only connected to a dangling track, it also will be removed
2007-08-10 19:14:51 +00:00
*/
bool TRACKS_CLEANER::deleteUnconnectedTracks()
2007-05-06 16:03:28 +00:00
{
if( m_Brd->m_Track == NULL )
return false;
bool modified = false;
bool item_erased = true;
while( item_erased ) // Iterate when at least one track is deleted
2007-08-10 19:14:51 +00:00
{
item_erased = false;
TRACK* next_track;
for( TRACK * track = m_Brd->m_Track; track ; track = next_track )
2007-08-10 19:14:51 +00:00
{
next_track = track->Next();
int flag_erase = 0; //Not connected indicator
int type_end = 0;
2007-08-10 19:14:51 +00:00
if( track->GetState( START_ON_PAD ) )
type_end |= START_ON_PAD;
if( track->GetState( END_ON_PAD ) )
type_end |= END_ON_PAD;
2008-03-10 15:00:22 +00:00
// if the track start point is not connected to a pad,
// test if this track start point is connected to another track
// For via test, an enhancement could be to test if connected
// to 2 items on different layers.
// Currently a via must be connected to 2 items, that can be on the same layer
LAYER_NUM top_layer, bottom_layer;
ZONE_CONTAINER* zone;
2007-08-10 19:14:51 +00:00
if( (type_end & START_ON_PAD ) == 0 )
{
TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_START );
2008-03-10 15:00:22 +00:00
if( other == NULL ) // Test a connection to zones
{
if( track->Type() != PCB_VIA_T )
{
zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
track->GetLayer(),
track->GetLayer(),
track->GetNetCode() );
}
else
{
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
top_layer, bottom_layer,
track->GetNetCode() );
}
}
if( (other == NULL) && (zone == NULL) )
{
flag_erase |= 1;
}
else // segment, via or zone connected to this end
{
track->start = other;
// If a via is connected to this end,
// test if this via has a second item connected.
// If no, remove it with the current segment
2007-08-10 19:14:51 +00:00
if( other && other->Type() == PCB_VIA_T )
{
// search for another segment following the via
track->SetState( BUSY, true );
SEGVIA* via = (SEGVIA*) other;
other = via->GetTrace( m_Brd->m_Track, NULL, FLG_START );
2007-08-10 19:14:51 +00:00
if( other == NULL )
{
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
via->LayerPair( &top_layer, &bottom_layer );
zone = m_Brd->HitTestForAnyFilledArea( via->GetStart(),
bottom_layer,
top_layer,
via->GetNetCode() );
}
if( (other == NULL) && (zone == NULL) )
flag_erase |= 2;
track->SetState( BUSY, false );
}
}
}
// if track end point is not connected to a pad,
// test if this track end point is connected to an other track
if( (type_end & END_ON_PAD ) == 0 )
2007-08-10 19:14:51 +00:00
{
TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_END );
if( other == NULL ) // Test a connection to zones
2007-08-10 19:14:51 +00:00
{
if( track->Type() != PCB_VIA_T )
{
zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
track->GetLayer(),
track->GetLayer(),
track->GetNetCode() );
}
else
{
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
top_layer, bottom_layer,
track->GetNetCode() );
}
2007-08-10 19:14:51 +00:00
}
if ( (other == NULL) && (zone == NULL) )
{
flag_erase |= 0x10;
}
else // segment, via or zone connected to this end
{
track->end = other;
// If a via is connected to this end, test if this via has a second item connected
// if no, remove it with the current segment
if( other && other->Type() == PCB_VIA_T )
{
// search for another segment following the via
2008-03-10 15:00:22 +00:00
track->SetState( BUSY, true );
2008-03-10 15:00:22 +00:00
SEGVIA* via = (SEGVIA*) other;
other = via->GetTrace( m_Brd->m_Track, NULL, FLG_END );
if( other == NULL )
{
* KIWAY Milestone A): Make major modules into DLL/DSOs. ! The initial testing of this commit should be done using a Debug build so that all the wxASSERT()s are enabled. Also, be sure and keep enabled the USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it off is senseless anyways. If you want stable code, go back to a prior version, the one tagged with "stable". * Relocate all functionality out of the wxApp derivative into more finely targeted purposes: a) DLL/DSO specific b) PROJECT specific c) EXE or process specific d) configuration file specific data e) configuration file manipulations functions. All of this functionality was blended into an extremely large wxApp derivative and that was incompatible with the desire to support multiple concurrently loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects. An amazing amount of organization come from simply sorting each bit of functionality into the proper box. * Switch to wxConfigBase from wxConfig everywhere except instantiation. * Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD, PGM_SINGLE_TOP, * Remove "Return" prefix on many function names. * Remove obvious comments from CMakeLists.txt files, and from else() and endif()s. * Fix building boost for use in a DSO on linux. * Remove some of the assumptions in the CMakeLists.txt files that windows had to be the host platform when building windows binaries. * Reduce the number of wxStrings being constructed at program load time via static construction. * Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that these functions are useful even when the wxConfigBase comes from another source, as is the case in the KICAD_MANAGER_FRAME. * Move the setting of the KIPRJMOD environment variable into class PROJECT, so that it can be moved into a project variable soon, and out of FP_LIB_TABLE. * Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all its child wxFrames and wxDialogs now have a Kiway() member function which returns a KIWAY& that that window tree branch is in support of. This is like wxWindows DNA in that child windows get this member with proper value at time of construction. * Anticipate some of the needs for milestones B) and C) and make code adjustments now in an effort to reduce work in those milestones. * No testing has been done for python scripting, since milestone C) has that being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
via->LayerPair( &top_layer, &bottom_layer );
zone = m_Brd->HitTestForAnyFilledArea( via->GetEnd(),
bottom_layer, top_layer,
via->GetNetCode() );
}
if( (other == NULL) && (zone == NULL) )
flag_erase |= 0x20;
2008-03-10 15:00:22 +00:00
track->SetState( BUSY, false );
}
2007-08-10 19:14:51 +00:00
}
}
if( flag_erase )
{
// remove segment from board
track->DeleteStructure();
// iterate, because a track connected to the deleted track
// is now perhaps now not connected and should be deleted
item_erased = true;
modified = true;
}
2007-08-10 19:14:51 +00:00
}
}
return modified;
2007-05-06 16:03:28 +00:00
}
2012-02-19 04:02:19 +00:00
// Delete null length segments, and intermediate points ..
bool TRACKS_CLEANER::clean_segments()
2007-05-06 16:03:28 +00:00
{
bool modified = false;
TRACK* segment, * nextsegment;
2007-10-19 19:58:31 +00:00
TRACK* other;
int flag, no_inc;
2007-08-10 19:14:51 +00:00
// Delete null segments
for( segment = m_Brd->m_Track; segment; segment = nextsegment )
2007-08-10 19:14:51 +00:00
{
nextsegment = segment->Next();
if( segment->IsNull() ) // Length segment = 0; delete it
segment->DeleteStructure();
2007-08-10 19:14:51 +00:00
}
// Delete redundant segments, i.e. segments having the same end points
// and layers
for( segment = m_Brd->m_Track; segment; segment = segment->Next() )
2007-08-10 19:14:51 +00:00
{
for( other = segment->Next(); other; other = nextsegment )
2007-08-10 19:14:51 +00:00
{
nextsegment = other->Next();
bool erase = false;
2007-08-10 19:14:51 +00:00
2007-10-19 19:58:31 +00:00
if( segment->Type() != other->Type() )
2007-08-10 19:14:51 +00:00
continue;
2008-03-10 15:00:22 +00:00
2007-10-19 19:58:31 +00:00
if( segment->GetLayer() != other->GetLayer() )
2007-08-10 19:14:51 +00:00
continue;
2008-03-10 15:00:22 +00:00
if( segment->GetNetCode() != other->GetNetCode() )
2007-08-10 19:14:51 +00:00
break;
if( ( segment->GetStart() == other->GetStart() ) &&
( segment->GetEnd() == other->GetEnd() ) )
erase = true;
2007-08-10 19:14:51 +00:00
if( ( segment->GetStart() == other->GetEnd() ) &&
( segment->GetEnd() == other->GetStart() ) )
erase = true;
2007-08-10 19:14:51 +00:00
2012-02-19 04:02:19 +00:00
// Delete redundant point
2007-08-10 19:14:51 +00:00
if( erase )
{
2007-10-19 19:58:31 +00:00
other->DeleteStructure();
modified = true;
2007-08-10 19:14:51 +00:00
}
}
}
// merge collinear segments:
for( segment = m_Brd->m_Track; segment; segment = nextsegment )
2007-08-10 19:14:51 +00:00
{
2007-10-19 19:58:31 +00:00
TRACK* segStart;
TRACK* segEnd;
2007-10-19 23:02:11 +00:00
TRACK* segDelete;
2007-08-10 19:14:51 +00:00
nextsegment = segment->Next();
if( segment->Type() != PCB_TRACE_T )
2007-08-10 19:14:51 +00:00
continue;
flag = no_inc = 0;
// search for a possible point connected to the START point of the current segment
2007-10-19 23:02:11 +00:00
for( segStart = segment->Next(); ; )
2007-08-10 19:14:51 +00:00
{
segStart = segment->GetTrace( segStart, NULL, FLG_START );
2007-10-19 19:58:31 +00:00
if( segStart )
2007-08-10 19:14:51 +00:00
{
2007-10-19 19:58:31 +00:00
// the two segments must have the same width
if( segment->GetWidth() != segStart->GetWidth() )
2007-08-10 19:14:51 +00:00
break;
2007-10-19 19:58:31 +00:00
// it cannot be a via
if( segStart->Type() != PCB_TRACE_T )
2007-08-10 19:14:51 +00:00
break;
2012-02-19 04:02:19 +00:00
// We must have only one segment connected
segStart->SetState( BUSY, true );
other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_START );
segStart->SetState( BUSY, false );
2007-08-10 19:14:51 +00:00
2007-10-19 19:58:31 +00:00
if( other == NULL )
2012-02-19 04:02:19 +00:00
flag = 1; // OK
2008-03-10 15:00:22 +00:00
2007-08-10 19:14:51 +00:00
break;
}
break;
}
if( flag ) // We have the starting point of the segment is connected to an other segment
2007-08-10 19:14:51 +00:00
{
segDelete = mergeCollinearSegmentIfPossible( segment, segStart, FLG_START );
2007-10-19 23:02:11 +00:00
if( segDelete )
2007-08-10 19:14:51 +00:00
{
no_inc = 1;
2007-10-19 23:02:11 +00:00
segDelete->DeleteStructure();
modified = true;
2007-08-10 19:14:51 +00:00
}
}
// search for a possible point connected to the END point of the current segment:
2007-10-19 23:02:11 +00:00
for( segEnd = segment->Next(); ; )
2007-08-10 19:14:51 +00:00
{
segEnd = segment->GetTrace( segEnd, NULL, FLG_END );
2007-10-19 19:58:31 +00:00
if( segEnd )
2007-08-10 19:14:51 +00:00
{
if( segment->GetWidth() != segEnd->GetWidth() )
2007-08-10 19:14:51 +00:00
break;
2008-03-10 15:00:22 +00:00
if( segEnd->Type() != PCB_TRACE_T )
2007-08-10 19:14:51 +00:00
break;
2012-02-19 04:02:19 +00:00
// We must have only one segment connected
segEnd->SetState( BUSY, true );
other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_END );
segEnd->SetState( BUSY, false );
2008-03-10 15:00:22 +00:00
2007-10-19 19:58:31 +00:00
if( other == NULL )
2012-02-19 04:02:19 +00:00
flag |= 2; // Ok
2008-03-10 15:00:22 +00:00
2007-08-10 19:14:51 +00:00
break;
}
else
{
2007-08-10 19:14:51 +00:00
break;
}
2007-08-10 19:14:51 +00:00
}
if( flag & 2 ) // We have the ending point of the segment is connected to an other segment
2007-08-10 19:14:51 +00:00
{
segDelete = mergeCollinearSegmentIfPossible( segment, segEnd, FLG_END );
2007-10-19 23:02:11 +00:00
if( segDelete )
2007-08-10 19:14:51 +00:00
{
2007-10-19 19:58:31 +00:00
no_inc = 1;
2007-10-19 23:02:11 +00:00
segDelete->DeleteStructure();
modified = true;
2007-08-10 19:14:51 +00:00
}
}
2012-02-19 04:02:19 +00:00
if( no_inc ) // The current segment was modified, retry to merge it
nextsegment = segment->Next();
2007-08-10 19:14:51 +00:00
}
return modified;
2007-05-06 16:03:28 +00:00
}
2007-08-10 19:14:51 +00:00
2012-02-19 04:02:19 +00:00
/** Function used by clean_segments.
* Test if aTrackRef and aCandidate (which must have a common end) are collinear.
* and see if the common point is not on a pad (i.e. if this common point can be removed).
* the ending point of aTrackRef is the start point (aEndType == START)
* or the end point (aEndType != START)
* flags START_ON_PAD and END_ON_PAD must be set before calling this function
* if the common point can be deleted, this function
* change the common point coordinate of the aTrackRef segm
* (and therefore connect the 2 other ending points)
* and return aCandidate (which can be deleted).
* else return NULL
2007-08-10 19:14:51 +00:00
*/
TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate,
int aEndType )
2007-05-06 16:03:28 +00:00
{
if( aTrackRef->GetWidth() != aCandidate->GetWidth() )
return NULL;
2007-08-10 19:14:51 +00:00
bool is_colinear = false;
2008-03-10 15:00:22 +00:00
// Trivial case: superimposed tracks ( tracks, not vias ):
if( aTrackRef->Type() == PCB_TRACE_T && aCandidate->Type() == PCB_TRACE_T )
{
if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
return aCandidate;
if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
return aCandidate;
}
int refdx = aTrackRef->GetEnd().x - aTrackRef->GetStart().x;
int refdy = aTrackRef->GetEnd().y - aTrackRef->GetStart().y;
int segmdx = aCandidate->GetEnd().x - aCandidate->GetStart().x;
int segmdy = aCandidate->GetEnd().y - aCandidate->GetStart().y;
2007-08-10 19:14:51 +00:00
// test for vertical alignment (easy to handle)
2007-08-10 19:14:51 +00:00
if( refdx == 0 )
{
if( segmdx != 0 )
return NULL;
else
is_colinear = true;
2007-08-10 19:14:51 +00:00
}
2008-03-10 15:00:22 +00:00
// test for horizontal alignment (easy to handle)
2007-08-10 19:14:51 +00:00
if( refdy == 0 )
{
if( segmdy != 0 )
return NULL;
else
is_colinear = true;
2007-08-10 19:14:51 +00:00
}
/* test if alignment in other cases
* We must have refdy/refdx == segmdy/segmdx, (i.e. same slope)
* or refdy * segmdx == segmdy * refdx
*/
if( is_colinear == false )
2007-08-10 19:14:51 +00:00
{
if( ( double)refdy * segmdx != (double)refdx * segmdy )
2007-08-10 19:14:51 +00:00
return NULL;
is_colinear = true;
2007-08-10 19:14:51 +00:00
}
/* Here we have 2 aligned segments:
* We must change the pt_ref common point only if not on a pad
* (this function) is called when there is only 2 connected segments,
*and if this point is not on a pad, it can be removed and the 2 segments will be merged
*/
if( aEndType == FLG_START )
2007-08-10 19:14:51 +00:00
{
// We do not have a pad, which is a always terminal point for a track
if( aTrackRef->GetState( START_ON_PAD) )
2007-08-10 19:14:51 +00:00
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() )
2007-08-10 19:14:51 +00:00
{
aTrackRef->SetStart( aCandidate->GetEnd());
aTrackRef->start = aCandidate->end;
aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( END_ON_PAD) );
return aCandidate;
2007-08-10 19:14:51 +00:00
}
else
2007-08-10 19:14:51 +00:00
{
aTrackRef->SetStart( aCandidate->GetStart() );
aTrackRef->start = aCandidate->start;
aTrackRef->SetState( START_ON_PAD, aCandidate->GetState( START_ON_PAD) );
return aCandidate;
2007-08-10 19:14:51 +00:00
}
}
else // aEndType == END
2007-08-10 19:14:51 +00:00
{
// We do not have a pad, which is a always terminal point for a track
if( aTrackRef->GetState( END_ON_PAD) )
2007-08-10 19:14:51 +00:00
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() )
2007-08-10 19:14:51 +00:00
{
aTrackRef->SetEnd( aCandidate->GetEnd() );
aTrackRef->end = aCandidate->end;
aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( END_ON_PAD) );
return aCandidate;
2007-08-10 19:14:51 +00:00
}
else
2007-08-10 19:14:51 +00:00
{
aTrackRef->SetEnd( aCandidate->GetStart() );
aTrackRef->end = aCandidate->start;
aTrackRef->SetState( END_ON_PAD, aCandidate->GetState( START_ON_PAD) );
return aCandidate;
2007-08-10 19:14:51 +00:00
}
}
2007-08-10 19:14:51 +00:00
return NULL;
2007-05-06 16:03:28 +00:00
}
bool PCB_EDIT_FRAME::RemoveMisConnectedTracks()
2007-05-06 16:03:28 +00:00
{
/* 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.
*/
2007-10-15 03:26:38 +00:00
TRACK* segment;
TRACK* other;
TRACK* next;
2007-08-10 19:14:51 +00:00
int net_code_s, net_code_e;
bool isModified = false;
2007-08-10 19:14:51 +00:00
for( segment = GetBoard()->m_Track; segment; segment = (TRACK*) segment->Next() )
2007-08-10 19:14:51 +00:00
{
segment->SetState( FLAG0, false );
2007-08-10 19:14:51 +00:00
2007-10-15 03:26:38 +00:00
// find the netcode for segment using anything connected to the "start" of "segment"
2007-08-10 19:14:51 +00:00
net_code_s = -1;
if( segment->start && segment->start->Type()==PCB_PAD_T )
2007-10-15 03:26:38 +00:00
{
// get the netcode of the pad to propagate.
net_code_s = ((D_PAD*)(segment->start))->GetNetCode();
2007-10-15 03:26:38 +00:00
}
2007-08-10 19:14:51 +00:00
else
{
other = segment->GetTrace( GetBoard()->m_Track, NULL, FLG_START );
2007-10-15 03:26:38 +00:00
if( other )
net_code_s = other->GetNetCode();
2007-08-10 19:14:51 +00:00
}
2008-03-10 15:00:22 +00:00
2007-08-10 19:14:51 +00:00
if( net_code_s < 0 )
2007-10-15 03:26:38 +00:00
continue; // the "start" of segment is not connected
2007-08-10 19:14:51 +00:00
2007-10-15 03:26:38 +00:00
// find the netcode for segment using anything connected to the "end" of "segment"
2007-08-10 19:14:51 +00:00
net_code_e = -1;
if( segment->end && segment->end->Type()==PCB_PAD_T )
2007-10-15 03:26:38 +00:00
{
net_code_e = ((D_PAD*)(segment->end))->GetNetCode();
2007-10-15 03:26:38 +00:00
}
2007-08-10 19:14:51 +00:00
else
{
other = segment->GetTrace( GetBoard()->m_Track, NULL, FLG_END );
2007-10-15 03:26:38 +00:00
if( other )
net_code_e = other->GetNetCode();
2007-08-10 19:14:51 +00:00
}
if( net_code_e < 0 )
2007-10-15 03:26:38 +00:00
continue; // the "end" of segment is not connected
2007-08-10 19:14:51 +00:00
// Netcodes do not agree, so mark the segment as "to be removed"
2007-08-10 19:14:51 +00:00
if( net_code_s != net_code_e )
{
segment->SetState( FLAG0, true );
2007-08-10 19:14:51 +00:00
}
}
// Remove tracks having a flagged segment
for( segment = GetBoard()->m_Track; segment; segment = next )
2007-08-10 19:14:51 +00:00
{
2007-10-15 03:26:38 +00:00
next = (TRACK*) segment->Next();
2008-03-10 15:00:22 +00:00
if( segment->GetState( FLAG0 ) ) // Segment is flagged to be removed
2007-08-10 19:14:51 +00:00
{
segment->SetState( FLAG0, false );
isModified = true;
GetBoard()->m_Status_Pcb = 0;
Remove_One_Track( NULL, segment );
// the current segment is deleted,
// we do not know the next "not yet tested" segment,
// so restart to the beginning
next = GetBoard()->m_Track;
2007-08-10 19:14:51 +00:00
}
}
return isModified;
2007-05-06 16:03:28 +00:00
}