Commit 7dc8fc1f authored by Lorenzo Marcantonio's avatar Lorenzo Marcantonio

More refactoring of the track cleanup code

Algorithm is still the same... now need to know what it really meant to do
parent 9fe5ce67
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
/** /**
* @file clean.cpp * @file clean.cpp
* @brief functions to clean tracks: remove null lenght and redundant segments * @brief functions to clean tracks: remove null length and redundant segments
*/ */
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
class TRACKS_CLEANER: CONNECTIONS class TRACKS_CLEANER: CONNECTIONS
{ {
private: private:
BOARD * m_Brd; BOARD *m_Brd;
public: public:
TRACKS_CLEANER( BOARD * aPcb ); TRACKS_CLEANER( BOARD * aPcb );
...@@ -52,8 +52,8 @@ public: ...@@ -52,8 +52,8 @@ public:
* the cleanup function. * the cleanup function.
* return true if some item was modified * return true if some item was modified
*/ */
bool CleanupBoard(PCB_EDIT_FRAME *aFrame, bool aCleanVias, bool CleanupBoard( PCB_EDIT_FRAME *aFrame, bool aCleanVias,
bool aMergeSegments, bool aDeleteUnconnected); bool aMergeSegments, bool aDeleteUnconnected);
private: private:
...@@ -63,13 +63,30 @@ private: ...@@ -63,13 +63,30 @@ private:
*/ */
bool clean_vias(); bool clean_vias();
/**
* Removes all the following THT vias on the same position of the
* specified one
*/
bool remove_duplicates_of_via( const VIA *aVia );
/**
* Removes all the following duplicates tracks of the specified one
*/
bool remove_duplicates_of_track( const TRACK *aTrack );
/** /**
* Removes dangling tracks * Removes dangling tracks
*/ */
bool deleteUnconnectedTracks(); bool deleteUnconnectedTracks();
/// Delete null length track segments
bool delete_null_segments();
/// Try to merge the segment to a following collinear one
bool merge_collinear_of_track( TRACK *aSegment );
/** /**
* Merge colinear segments and remove null len segments * Merge collinear segments and remove duplicated and null len segments
*/ */
bool clean_segments(); bool clean_segments();
...@@ -193,6 +210,28 @@ void TRACKS_CLEANER::buildTrackConnectionInfo() ...@@ -193,6 +210,28 @@ void TRACKS_CLEANER::buildTrackConnectionInfo()
} }
} }
bool TRACKS_CLEANER::remove_duplicates_of_via( const VIA *aVia )
{
bool modified = false;
// Search and delete others vias at same location
VIA* next_via;
for( VIA* alt_via = GetFirstVia( aVia->Next() ); alt_via != NULL;
alt_via = next_via )
{
next_via = GetFirstVia( alt_via->Next() );
if( (alt_via->GetViaType() == VIA_THROUGH) &&
(alt_via->GetStart() == aVia->GetStart()) )
{
// delete via
alt_via->DeleteStructure();
modified = true;
}
}
return modified;
}
bool TRACKS_CLEANER::clean_vias() bool TRACKS_CLEANER::clean_vias()
{ {
bool modified = false; bool modified = false;
...@@ -211,21 +250,7 @@ bool TRACKS_CLEANER::clean_vias() ...@@ -211,21 +250,7 @@ bool TRACKS_CLEANER::clean_vias()
* (yet) handle high density interconnects */ * (yet) handle high density interconnects */
if( via->GetViaType() != VIA_THROUGH ) if( via->GetViaType() != VIA_THROUGH )
{ {
// Search and delete others vias at same location modified |= remove_duplicates_of_via( via );
VIA* next_via;
for( VIA* alt_via = GetFirstVia( via->Next() ); alt_via != NULL;
alt_via = next_via )
{
next_via = GetFirstVia( alt_via->Next() );
if( (alt_via->GetViaType() == VIA_THROUGH) &&
(alt_via->GetStart() == via->GetStart()) )
{
// delete via
alt_via->DeleteStructure();
modified = true;
}
}
/* To delete through Via on THT pads at same location /* To delete through Via on THT pads at same location
* Examine the list of connected pads: * Examine the list of connected pads:
...@@ -360,16 +385,14 @@ bool TRACKS_CLEANER::deleteUnconnectedTracks() ...@@ -360,16 +385,14 @@ bool TRACKS_CLEANER::deleteUnconnectedTracks()
return modified; return modified;
} }
// Delete null length track segments
// Delete null length segments, and intermediate points .. bool TRACKS_CLEANER::delete_null_segments()
bool TRACKS_CLEANER::clean_segments()
{ {
TRACK *nextsegment;
bool modified = false; bool modified = false;
TRACK* segment, * nextsegment;
TRACK* other;
// Delete null segments // Delete null segments
for( segment = m_Brd->m_Track; segment; segment = nextsegment ) for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
{ {
nextsegment = segment->Next(); nextsegment = segment->Next();
...@@ -379,145 +402,211 @@ bool TRACKS_CLEANER::clean_segments() ...@@ -379,145 +402,211 @@ bool TRACKS_CLEANER::clean_segments()
modified = true; modified = true;
} }
} }
return modified;
}
// Delete redundant segments, i.e. segments having the same end points bool TRACKS_CLEANER::remove_duplicates_of_track( const TRACK *aTrack )
// and layers {
for( segment = m_Brd->m_Track; segment; segment = segment->Next() ) bool modified = false;
TRACK *nextsegment;
for( TRACK *other = aTrack->Next(); other; other = nextsegment )
{ {
for( other = segment->Next(); other; other = nextsegment ) nextsegment = other->Next();
// New netcode, break out (can't be there any other)
if( aTrack->GetNetCode() != other->GetNetCode() )
break;
// 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()) )
{ {
nextsegment = other->Next(); if( ((aTrack->GetStart() == other->GetStart()) &&
bool erase = false; (aTrack->GetEnd() == other->GetEnd())) ||
((aTrack->GetStart() == other->GetEnd()) &&
(aTrack->GetEnd() == other->GetStart())))
{
other->DeleteStructure();
modified = true;
}
}
}
return modified;
}
bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
{
bool merged_this = false;
bool flag = false; // If there are connections to this on the endpoint
if( segment->Type() != other->Type() ) // search for a possible point connected to the START point of the current segment
continue; TRACK *segStart = aSegment->Next();
while( true )
{
segStart = aSegment->GetTrack( segStart, NULL, ENDPOINT_START );
if( segment->GetLayer() != other->GetLayer() ) if( segStart )
continue; {
// the two segments must have the same width
if( aSegment->GetWidth() != segStart->GetWidth() )
break;
if( segment->GetNetCode() != other->GetNetCode() ) // it cannot be a via
if( segStart->Type() != PCB_TRACE_T )
break; break;
if( ( segment->GetStart() == other->GetStart() ) && // We must have only one segment connected
( segment->GetEnd() == other->GetEnd() ) ) segStart->SetState( BUSY, true );
erase = true; TRACK *other = aSegment->GetTrack( m_Brd->m_Track, NULL, ENDPOINT_START );
segStart->SetState( BUSY, false );
if( ( segment->GetStart() == other->GetEnd() ) && if( other == NULL )
( segment->GetEnd() == other->GetStart() ) ) flag = true; // OK
erase = true;
// Delete redundant point break;
if( erase )
{
other->DeleteStructure();
modified = true;
}
} }
break;
} }
// merge collinear segments: if( flag ) // We have the starting point of the segment is connected to an other segment
for( segment = m_Brd->m_Track; segment; segment = nextsegment )
{ {
TRACK* segStart; TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment, segStart, ENDPOINT_START );
TRACK* segEnd;
TRACK* segDelete;
nextsegment = segment->Next(); if( segDelete )
{
segDelete->DeleteStructure();
merged_this = true;
}
}
if( segment->Type() != PCB_TRACE_T ) // Do the same with the other endpoint
continue; flag = false;
unsigned flag = 0; // search for a possible point connected to the END point of the current segment:
bool no_inc = false; TRACK *segEnd = aSegment->Next();
while( true )
{
segEnd = aSegment->GetTrack( segEnd, NULL, ENDPOINT_END );
// search for a possible point connected to the START point of the current segment if( segEnd )
for( segStart = segment->Next(); ; )
{ {
segStart = segment->GetTrack( segStart, NULL, ENDPOINT_START ); if( aSegment->GetWidth() != segEnd->GetWidth() )
break;
if( segStart )
{
// the two segments must have the same width
if( segment->GetWidth() != segStart->GetWidth() )
break;
// it cannot be a via if( segEnd->Type() != PCB_TRACE_T )
if( segStart->Type() != PCB_TRACE_T ) break;
break;
// We must have only one segment connected // We must have only one segment connected
segStart->SetState( BUSY, true ); segEnd->SetState( BUSY, true );
other = segment->GetTrack( m_Brd->m_Track, NULL, ENDPOINT_START ); TRACK *other = aSegment->GetTrack( m_Brd->m_Track, NULL, ENDPOINT_END );
segStart->SetState( BUSY, false ); segEnd->SetState( BUSY, false );
if( other == NULL ) if( other == NULL )
flag = 1; // OK flag = true; // Ok
break;
}
break; break;
} }
else
if( flag ) // We have the starting point of the segment is connected to an other segment
{ {
segDelete = mergeCollinearSegmentIfPossible( segment, segStart, ENDPOINT_START ); break;
if( segDelete )
{
no_inc = 1;
segDelete->DeleteStructure();
modified = true;
}
} }
}
// search for a possible point connected to the END point of the current segment: if( flag ) // We have the ending point of the segment is connected to an other segment
for( segEnd = segment->Next(); ; ) {
{ TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment, segEnd, ENDPOINT_END );
segEnd = segment->GetTrack( segEnd, NULL, ENDPOINT_END );
if( segEnd ) if( segDelete )
{ {
if( segment->GetWidth() != segEnd->GetWidth() ) segDelete->DeleteStructure();
break; merged_this = true;
}
}
return merged_this;
}
if( segEnd->Type() != PCB_TRACE_T ) // Delete null length segments, and intermediate points ..
break; bool TRACKS_CLEANER::clean_segments()
{
bool modified = false;
// We must have only one segment connected // Easy things first
segEnd->SetState( BUSY, true ); modified |= delete_null_segments();
other = segment->GetTrack( m_Brd->m_Track, NULL, ENDPOINT_END );
segEnd->SetState( BUSY, false );
if( other == NULL ) // Delete redundant segments, i.e. segments having the same end points
flag |= 2; // Ok // and layers
for( TRACK *segment = m_Brd->m_Track; segment; segment = segment->Next() )
{
modified |= remove_duplicates_of_track( segment );
}
break; // merge collinear segments:
} TRACK *nextsegment;
else for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
{ {
break; nextsegment = segment->Next();
}
}
if( flag & 2 ) // We have the ending point of the segment is connected to an other segment if( segment->Type() == PCB_TRACE_T )
{ {
segDelete = mergeCollinearSegmentIfPossible( segment, segEnd, ENDPOINT_END ); bool merged_this = merge_collinear_of_track( segment );
modified |= merged_this;
if( segDelete ) if( merged_this ) // The current segment was modified, retry to merge it
{ nextsegment = segment->Next();
no_inc = true;
segDelete->DeleteStructure();
modified = true;
}
} }
if( no_inc ) // The current segment was modified, retry to merge it
nextsegment = segment->Next();
} }
return modified; return modified;
} }
/* Utility: check for parallelism between two segments */
static bool parallelism_test( int dx1, int dy1, int dx2, int dy2 )
{
// The following condition tree is ugly and repetitive, but I have
// not a better way to express clearly the trivial cases. Hope the
// compiler optimize it better than always doing the product
// below...
// test for vertical alignment (easy to handle)
if( dx1 == 0 )
{
if( dx2 != 0 )
return false;
else
return true;
}
if( dx2 == 0 )
{
if( dx1 != 0 )
return false;
else
return true;
}
// test for horizontal alignment (easy to handle)
if( dy1 == 0 )
{
if( dy2 != 0 )
return false;
else
return true;
}
if( dy2 == 0 )
{
if( dy1 != 0 )
return false;
else
return true;
}
/* test for alignment in other cases: Do the usual cross product test
* (the same as testing the slope, but without a division) */
return ((double)dy1 * dx2 == (double)dx1 * dy2);
}
/** Function used by clean_segments. /** Function used by clean_segments.
* Test if aTrackRef and aCandidate (which must have a common end) are collinear. * Test if aTrackRef and aCandidate (which must have a common end) are collinear.
...@@ -534,63 +623,32 @@ bool TRACKS_CLEANER::clean_segments() ...@@ -534,63 +623,32 @@ bool TRACKS_CLEANER::clean_segments()
TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate, TRACK* TRACKS_CLEANER::mergeCollinearSegmentIfPossible( TRACK* aTrackRef, TRACK* aCandidate,
ENDPOINT_T aEndType ) ENDPOINT_T aEndType )
{ {
if( aTrackRef->GetWidth() != aCandidate->GetWidth() ) // 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) )
return NULL; return NULL;
bool is_colinear = false; // Trivial case: exactly the same track
if( ( aTrackRef->GetStart() == aCandidate->GetStart() ) &&
// Trivial case: superimposed tracks ( tracks, not vias ): ( aTrackRef->GetEnd() == aCandidate->GetEnd() ) )
if( aTrackRef->Type() == PCB_TRACE_T && aCandidate->Type() == PCB_TRACE_T ) return aCandidate;
{
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; if( ( aTrackRef->GetStart() == aCandidate->GetEnd() ) &&
int refdy = aTrackRef->GetEnd().y - aTrackRef->GetStart().y; ( aTrackRef->GetEnd() == aCandidate->GetStart() ) )
return aCandidate;
int segmdx = aCandidate->GetEnd().x - aCandidate->GetStart().x; // Weed out non-parallel tracks
int segmdy = aCandidate->GetEnd().y - aCandidate->GetStart().y; if ( !parallelism_test( aTrackRef->GetEnd().x - aTrackRef->GetStart().x,
aTrackRef->GetEnd().y - aTrackRef->GetStart().y,
// test for vertical alignment (easy to handle) aCandidate->GetEnd().x - aCandidate->GetStart().x,
if( refdx == 0 ) aCandidate->GetEnd().y - aCandidate->GetStart().y ) )
{ return NULL;
if( segmdx != 0 )
return NULL;
else
is_colinear = true;
}
// test for horizontal alignment (easy to handle)
if( refdy == 0 )
{
if( segmdy != 0 )
return NULL;
else
is_colinear = true;
}
/* 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 )
{
if( ( double)refdy * segmdx != (double)refdx * segmdy )
return NULL;
is_colinear = true;
}
/* Here we have 2 aligned segments: /* Here we have 2 aligned segments:
* We must change the pt_ref common point only if not on a pad * 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, * (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 * and if this point is not on a pad, it can be removed and the 2 segments will be merged
*/ */
if( aEndType == ENDPOINT_START ) if( aEndType == ENDPOINT_START )
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment